Developer Guide
Introduction
Welcome! We are excited that you want to learn about TBM.
This page contains step-by-step instructions on how to get started in contributing or getting yourself involved in this awesome project! However, before you get too excited, allow us to introduce you to the core concepts that underpins the heart of TBM.
Travelling BusinessMan Design Concepts
-
Lightweight/Portable: TBM has a small footprint and runs fast on any platform.
-
Scalable: Software which does not deteriorate in performance with increasing data sizes.
-
Extensible: A community driven software development process, which encourages innovative inputs and contributions from our users. Features are easily integrated into the application when needed.
-
Privacy: We do not collect, store or misappropriate our client’s data.
-
CLI Optimised: Built by users for users. Our features in the application cater to users highly accustomed to the CLI.
These concepts are the foundations of TBM, furthermore we believe in the value of having users being an active contributor to our project. With that being said, this guide will help orient you eager developers quickly, to things like,
-
Software design choices of TBM.
-
Architecture of TBM.
-
Implementations for the array of features TBM offers.
Table of Contents
- Introduction
- Table of Contents
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
Architecture

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
.puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Main has two classes called Main and MainApp. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI: The UI of the App. -
Logic: The command executor. -
Model: Holds the data of the App in memory. -
Storage: Reads data from, and writes data to, the hard disk.
Each of the four components,
- defines its API in an
interfacewith the same name as the Component. - exposes its functionality using a concrete
{Component Name}Managerclass (which implements the corresponding APIinterfacementioned in the previous point.
For example, the Logic component (see the class diagram given below) defines its API in the Logic.java interface and exposes its functionality using the LogicManager.java class which implements the Logic interface.

How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command client delete 1.

The sections below give more details of each component.
UI component

API :
Ui.java
The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, ClientListPanel, WidgetViewBox, StatusBarFooter, etc. All these, including the MainWindow, inherit from the abstract UiPart class.
The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml
The UI component,
- Executes user commands using the
Logiccomponent. - Listens for changes to
Modeldata so that the UI can be updated with the modified data.
Logic component

API :
Logic.java
-
Logicuses theMainParserclass to parse the user command. - This results in a
Commandobject which is executed by theLogicManager. - The command execution can affect the
Model(e.g. adding a client). - The result of the command execution is encapsulated as a
CommandResultobject which is passed back to theUi. - In addition, the
CommandResultobject can also instruct theUito perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call.

DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model component

API : Model.java
The Model,
- stores a
UserPrefobject that represents the user’s preferences. - stores the address book data.
- exposes an unmodifiable
ObservableList<Client>that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - does not depend on any of the other three components.
- contains a
UniqueTagSetto prevent duplication ofTagobjects.
Inner Workings of TBM

The above Model diagram provides a high level view of how TBM functions. However, is does not fully represent the class design between Note, Country and Tag. This sub-section gives a better explanation of the mappings for Note, Tag and Country.
Storage component

API : Storage.java
The Storage component,
- can save
UserPrefobjects in json format and read it back. - can save the address book data in json format and read it back.
Common classes
Classes used by multiple components are in the seedu.address.commons package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Associating Notes and Tags
The proposed association mechanism between Tag and Note objects is facilitated by TagNoteMap.
It is stored internally within the TbmManager object. Additionally, it implements the following operations:
-
TagNoteMap#getTagsForNote()— Returns an unmodifiable set of Tags associated to a particular Note. -
TagNoteMap#updateTagsForNote()— Returns an unmodifiable set of Notes associated to a particular Tag . -
TagNoteMap#initTagNoteMapFromNotes()— Initialises the TagNoteMap from a set of Notes.
The TagNoteMap#initTagNoteMapFromNotes() is exposed in the Model interface as Model#initialiseTagNoteMap().
Given below is an example usage scenario and how mapping mechanism behaves at each step.
Associating Notes and Countries
Implementation
The association between Note and Country is facilitated by CountryNotesManager.
Internally, CountryNotesManager stores a list of country notes using an instance of javafx.collections.ObservableList<CountryNote>.
By storing the list of country notes as an ObservableList, the UI would be able to track and immediately reflect any changes to the country notes list.
It implements the following operations:
-
CountryNotesManager#asUnmodifiableObservableList()— Returns an unmodifiableObservableList<CountryNote>. -
CountryNotesManager#hasCountryNote(CountryNote countryNote)— Returns true if the givencountryNotealready exists in the internalObservableList<CountryNote>. -
CountryNotesManager#addCountryNote(CountryNote countryNote)— Adds the givencountryNoteto the internalObservableList<CountryNote>. -
CountryNotesManager#deleteCountryNote(CountryNote countryNote)— Deletes the givencountryNotefrom the internalObservableList<CountryNote>.
The following class diagram illustrates how the relevant classes in the seedu.address.model package are related to CountryNote.

Given below is a sequence diagram that shows how the country note add command works.
For brevity, the full command country note add c/COUNTRY_CODE nt/NOTE_STRING will be substituted by country note add.
Note that the AddressBookParser#parseCommand(userInput) calls CountryNoteAddCommandParser#parse(userInput) which in turn parses the user input into a CountryNote object, and returns an instance of a CountryNoteAddCommand with the CountryNote instance passed in as an argument to the constructor of CountryNoteAddCommand.
Hence, CountryNoteAddCommand stores a CountryNote object. For brevity, the aforementioned sequence of method calls will be excluded from the following sequence diagram.

Suggesting contacts
Implementation
The suggestion mechanism is facilitated by filteredClients in ModelManager. It is an instance of javafx.collections.transformation.FilteredList<Client>. It implements the following relevant operations:
-
FilteredList<Client>#setPredicate(Predicate<? super Client> p)— Filters out any clients that do not match the predicate in the list. -
FilteredList<Client>#sort(Comparator<? super Client> p)— Sorts the list.
These operations are exposed in the Model interface as Model#updateFilteredClientList() and Model#sortFilteredClientList() respectively.
The following activity diagram summarizes what happens when a user inputs a suggest command.

Given below is an example usage scenario and how the suggestion mechanism behaves at each step.
Step 1: The user executes suggest by/contract to list the suggested clients sorted by contract expiry dates. At this point, filteredClients is showing all clients.

Step 2: The suggest command calls Model#updateFilteredClientList with the contract expiry date predicate (which checks if a client has a contract expiry date). Model updates the filteredClients object with the contract expiry date predicate which filters out all clients without an existing contract expiry date.

Step 3: The suggest command calls Model#sortFilteredClientList with the contract expiry date comparator (which sorts clients by earliest contract expiry date). Model updates the filteredClients object with the contract expiry date comparator which gives us clients in order of increasing contract expiry date.

Step 4: The change is then propagated to Ui, which updates the displayed clients in ClientListPanel.
Step 5: The user decides to execute the command list, which resets the filteredClients objects to have all clients, and in turn resets the displayed clients in ClientListPanel as well.
The following sequence diagram shows how the suggest operation works:

[Proposed] Undo/redo feature
Proposed Implementation
The proposed undo/redo mechanism is facilitated by VersionedTbmManager. It extends TbmManager with an undo/redo history, stored internally as an tbmManagerStateList and currentStatePointer. Additionally, it implements the following operations:
-
VersionedTbmManager#commit()— Saves the current address book state in its history. -
VersionedTbmManager#undo()— Restores the previous address book state from its history. -
VersionedTbmManager#redo()— Restores a previously undone address book state from its history.
These operations are exposed in the Model interface as Model#commitTbmManager(), Model#undoTbmManager() and Model#redoTbmManager() respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedTbmManager will be initialized with the initial address book state, and the currentStatePointer pointing to that single address book state.

Step 2. The user executes delete 5 command to delete the 5th client in the address book. The delete command calls Model#commitTbmManager(), causing the modified state of the address book after the delete 5 command executes to be saved in the tbmManagerStateList, and the currentStatePointer is shifted to the newly inserted address book state.

Step 3. The user executes add n/David … to add a new client. The add command also calls Model#commitTbmManager(), causing another modified address book state to be saved into the tbmManagerStateList.

Model#commitTbmManager(), so the address book state will not be saved into the tbmManagerStateList.
Step 4. The user now decides that adding the client was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undoTbmManager(), which will shift the currentStatePointer once to the left, pointing it to the previous address book state, and restores the address book to that state.

currentStatePointer is at index 0, pointing to the initial TbmManager state, then there are no previous TbmManager states to restore. The undo command uses Model#canUndoTbmManager() to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how the undo operation works:

UndoCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo command does the opposite — it calls Model#redoTbmManager(), which shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the address book to that state.
currentStatePointer is at index tbmManagerStateList.size() - 1, pointing to the latest address book state, then there are no undone TbmManager states to restore. The redo command uses Model#canRedoTbmManager() to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command list. Commands that do not modify the address book, such as list, will usually not call Model#commitTbmManager(), Model#undoTbmManager() or Model#redoTbmManager(). Thus, the tbmManagerStateList remains unchanged.

Step 6. The user executes clear, which calls Model#commitTbmManager(). Since the currentStatePointer is not pointing at the end of the tbmManagerStateList, all address book states after the currentStatePointer will be purged. Reason: It no longer makes sense to redo the add n/David … command. This is the behavior that most modern desktop applications follow.

The following activity diagram summarizes what happens when a user executes a new command:

Design consideration:
Aspect: How undo/redo executes
-
Alternative 1 (current choice): Saves the entire address book.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
delete, just save the client being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
{more aspects and alternatives to be added}
[Proposed] Data archiving
{Explain here how the data archiving feature will be implemented}
[Proposed] Command History
A command history will greatly optimise the user’s productivity by removing the need to repetitively type out similar commands. Furthermore, it allows backwards viewing of previous commands given, which is similar to the CLI experience.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile: Businesspeople who travel a lot
- has a need to manage a significant number of clients that span multiple timezones
- prefers desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: Manages notes, client contact details and preferences across multiple countries and timezones
User stories
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
* * * |
potential user | see the App populated with sample data | easily see how the App will look like when it is in use |
* * * |
user ready to start using the App | purge all current data | get rid of sample/experimental data I used to explore the app |
* * * |
new user | see usage instructions | refer to instructions when I forget how to use the App |
* * * |
user | add contact details of business contacts | |
* * * |
user | add personal preferences of existing business contacts | keep track of them |
* * * |
user | add meeting notes after meeting with existing business contacts | keep track of my meetings |
* * * |
user | add information on business deals and contract expiration dates | let TBM keep track of them |
* * * |
user | filter business contacts by country | |
* * * |
user | add cultural or relevant notes regarding a specific country | refer to these notes in the future |
* * * |
user | view my previously added information regarding a country | |
* * * |
user | see and search for all my clients | easily find and view information on my clients without having to go through the entire list |
* * * |
user | edit my clients’ information | |
* * * |
user | save the information I enter | |
* * * |
user | transfer my data across different computers | |
* * * |
user | manage the priority of business aspects regarding my clients (e.g. expiring contracts) | |
* * * |
user | receive suggestions on which clients to catch up on if I have not done so for quite long | maintain my relationship with my clients |
* * * |
user | view who is likely to be free in other timezones currently | contact them and catch up with them |
* * * |
user | delete a client | remove entries that I no longer need |
* * * |
user | note down and be reminded when my clients have their noteworthy moments (dates) | congratulate them in a timely manner |
* * |
long-time user | archive/hide unused data | not be distracted by irrelevant data |
* * |
user | manage my flight bookings across countries | |
* * |
user | manage my hotel bookings in a particular country | |
* * |
user | visualize my flight and hotel bookings on a timeline | |
* * |
user | filter business contacts by country and state | |
* * |
user | undo and redo | correct my mistakes easily |
* |
user | hide private contact details | minimize chance of someone else seeing them by accident |
* |
user | find clients nearest to my current location | find someone near me to meet |
* |
user | customize the GUI | make it more intuitive to use or more visually appealing |
* |
proficient CLI user | type out my emails straight from the application without moving to the browser | have a faster workflow |
Use cases
(For all use cases below, the System is the TBM and the Actor is the user, unless specified otherwise)
UC1 - Adding a Client
MSS
- User meets secures a new business deal/client
- User attempts to add the all associated parties and their information into TBM.
-
User successfully adds the all new information into TBM.
Use case ends.
Extensions
-
2a. A party has a prior entry in TBM, which shows that User has a previous professional encounter with the party.
- 2a1. User does not add the new party as it will duplicate entries in the TBM.
- 2a2. User can choose to update/edit the client information instead.
Use case ends.
UC2 - Finding Clients
MSS
- User requests to find a client.
-
TBM shows a list of clients that match user’s query.
Use case ends.
Extensions
-
1a. The list of clients is empty.
Use case ends.
UC3 - Saving data
MSS
- User enters a valid command that alters data (E.g. `adding a client (UC1)`).
- Modified data gets stored in the existing data file.
-
TBM shows a message indicating command has been executed successfully.
Use case ends.
Extensions
-
2a. An error occurred while saving the modified data to the existing data file.
- 2a1. TBM reloads the data from the existing data file.
- 2a2. TBM shows an error message.
Use case ends.
UC4 - Filtering by country
MSS
- User inputs a country as filter.
-
TBM shows all clients belonging to that country.
Use case ends.
Extensions
-
1a. Invalid country is given.
-
1a1. TBM shows an error message.
Use case ends.
-
UC5 - Clearing all entries from **TBM**
MSS
- User requests to clear all entries.
- TBM asks the user to confirm.
- User confirms that they want to clear all entries.
-
TBM clears all entries.
Use case ends.
Extensions
-
1a. There are no entries.
Use case ends.
-
3a. User decides not to clear all entries.
Use case ends.
{More to be added}
Non-Functional Requirements
-
TBM should work on any mainstream OS as long as it has Java
11or above installed. - A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
- TBM should be able to recover its previous stable state from the data file if it crashes.
- The data file should be stored locally in a human-editable text file.
- TBM should be used by a single user.
- TBM should be able to hold up to 1000 business contacts without a noticeable sluggishness in performance for typical usage.
- TBM should be able to hold up to 5000 total client notes without a noticeable sluggishness in performance for typical usage.
- TBM can handle at most 10000 business contacts and at most 50000 total client notes.
- TBM will only accept countries that are specified by the ISO3166 specification.
- TBM will only accept GMT timezones.
- TBM is not required to validate that the timezone of a business contact correctly matches his/her country.
- TBM should retain all functionalities even when it is not connected to the internet.
- The size of the TBM JAR file should not exceed 100Mb.
Glossary
- Business Contact: Synonymous with Client
- Client: Refers to a person whom the user is conducting his/her business with
- GMT: Greenwich Mean Time
- Mainstream OS: Windows, Linux, Unix, macOS
- TBM: Initialism for Travelling BusinessMan
Appendix: Instructions for manual testing
Given below are instructions to test the app manually. Additionally, testers can look through our Testing Guide to get started on automated testing.
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
-
{ more test cases … }
Deleting a client
-
Deleting a client while all clients are being shown
-
Prerequisites: List all clients using the
listcommand. Multiple clients in the list. -
Test case:
client delete 1
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -
Test case:
client delete 0
Expected: No client is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
client delete,client delete x,...(where x is larger than the list size)
Expected: Similar to previous.
-
-
{ more test cases … }
Saving data
-
Dealing with missing/corrupted data files
- {explain how to simulate a missing/corrupted file, and the expected behavior}
-
{ more test cases … }