Domain Modelling

Data are the soul of an app, that's why we should tackle domain modelling right away.

gohkhoonhiang
Apr 24, 2023 | 24 mins read

Domain Modelling

Now that we have the requirements of the app, let’s start with defining the domain models.

Domain models are the essence of an app. If the models are defined as fitting as possible, it will make implementing the business logic more seamless later. If the models are designed to be as generic as possible, it will make it less painful to change the implementation later.

Of course, we cannot possibly predict everything that will happen during the course of implementation, and we need to be prepared that some of the initial designs will have to change. Which is why, it will be better if we do not make too much assumption about what web framework or datastore mechanism will be used for actual implementation during the modelling stage.

Having said that, for the convenience of documenting the domain models, we will use json data type for key-value or array type fields. Not all datastores can support JSON data type natively, and if that is the case, then we will map the key-value or array type field to a separate model.

Similarly, for the convenience of documentation, we use ERD which implies the use of a relational database, but we should be able to use any type of datastores if we need to.

With that, let’s get into the details of the models that we have identified for this app.

Let’s get going with the design.

Transactions

At the core of the app is all about Transactions. There are a few types of transactions:

  • Income Transactions: These are transactions as a result of receiving payments for offering services or products.
  • Expense Transactions: These are transactions as a result of making payments for another party’s services or products.
  • Income Reversal Transactions: These are transactions as a result of reversing an income transaction.
  • Expense Reversal Transactions: These are transactions as a result of reversing an expense transaction.

By default, home currency amount will be automatically calculated based on the exchange rate between the home Currency and the transaction’s Currency. However, the user should also have the flexibility to overwrite the amount if there’s a specific amount determined by the service provider.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Transactions {
        text id PK
        text type "not null"
        date transaction_date "not null"
        text description "not null"
        numeric amount "not null"
        numeric home_currency_amount "not null"
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text currency_id FK "not null"
        text associated_transaction_id FK
    }

Transaction models.

Tags

One of the critical features of this app is the use of Tags. Tags can be used to filter Work Logs to generate Invoices, or render charts, or use for tax computation. For this reason, we model Tag as an explicit entity.

Each Tag can have a category and a name. The pair needs to be unique as it will form the composite primary key of each record. By default, the system will render the Tag with the format category:name. The user can customise the format under System Config.

For display distinction, the user can customise the color scheme of each Tag. By default, the system will render all Tags with white text on grey background, like this company:company-abc.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Tags {
        text category "not null, default: ''"
        text name "not null, composite PK and unique constraint: [category, name]"
        text description
        text text_color "default: 'white'"
        text background_color "default: 'grey'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Tag model.

Work Logs

Another main feature of the app is the capability to track work done so that the time logs can be used to generate Invoices to another party.

For invoicing purpose, the user can input a short description. If she wants to log the details of the work done, she can do so with the content field.

The user can specify more than one Tag associated with the Work Log.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    WorkLogs {
        text id PK
        timestamp start_time "not null"
        timestamp end_time "not null"
        text description "not null"
        text content
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Work Log model.

Invoice Configs and Invoice Templates

In order to generate Invoices, the user can configure the properties of the Invoice to each individual party.

The tags field tells the system to filter only specific Work Logs with those Tags to be included in the generated Invoice.

The custom fields will be propagated to each instance of Invoices when it’s generated. This allows the user to configure fields that can be used in the Invoice Template to display additional information that is not directly related to the Work Logs.

In order to render the Invoice, the user can choose from built-in templates or create custom template using some form of markup and styles languages (we do not make any assumption at this point regarding the actual languages to use).

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    InvoiceConfigs {
        text id PK
        text description "not null"
        text invoice_cycle_date "not null, default: 1"
        text invoice_cycle_duration_value "not null, default: 1"
        text invoice_cycle_duration_unit "not null, default: 'month'"
        numeric due_date_cycle_value "not null, default: 30"
        text due_date_cycle_unit "not null, default: 'day'"
        text payment_terms
        json tags "not null, default: '[]'"
        json custom_fields "not null, default: '{}'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text invoice_number_sequence_id FK "not null"
        text billable_contact_id FK "not null"
        text invoice_template_id FK "not null"
    }

    InvoiceTemplates {
        text id PK
        text name "not null"
        text content_markup "not null"
        text content_styles "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Invoice Config and Invoice Template models.

Invoices and Invoice Lines

Then each instance of the Invoices generated will be tracked individually. Each Invoice can contain one or more lines for the calculation of the total sum.

By default, the Invoice Lines are generated from the Work Logs using the predefined Billing Configs for the billing Contact. If the user needs to include additional costs, then she can create new Invoice Lines accordingly, and use the Invoice Template to customise the display of these custom Invoice Lines.

There is also another possible scenario where the Invoice doesn’t involve any Work Log. In this case, the user can simply manually create an Invoice and custom Invoice Lines.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Invoices {
        text id PK
        text invoice_number "not null"
        date invoice_date "not null"
        date due_date "not null"
        numeric total_amount "not null"
        json custom_fields "not null, default: '{}'"
        bool voided "not null, default: false"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text invoice_config_id FK "not null"
        text currency_id FK "not null"
        text contact_id FK "not null"
    }

    InvoiceLines {
        text id PK
        text description "not null"
        text unit "not null"
        numeric unit_cost "not null"
        numeric unit_value "not null"
        numeric subtotal "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text invoice_id FK "not null"
    }

Invoice and Invoice Line models.

Income Receipt Configs and Income Receipt Templates

Alongside the Invoices, the user can also configure the properties of the Income Receipt as a result of payment based on the issued Invoice.

The custom fields in Income Receipt Configs will be propagated to each instance of Income Receipt when it’s generated. This allows the user to configure fields that can be used in the Income Receipt Template to display additional information that is not directly related to the associated Invoice.

In order to render the Income Receipt, the user can choose from built-in templates or create custom template using markup and styles languages.

All fields from the associated Invoice can be used in the Income Receipt Template in addition to the Income Receipt Config fields.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    IncomeReceiptConfigs {
        text id PK
        json custom_fields "not null, default: '{}'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text receipt_number_sequence_id FK "not null"
        text billable_contact_id FK "not null"
        text income_receipt_template_id FK "not null"
    }

    IncomeReceiptTemplates {
        text id PK
        text name "not null"
        text content_markup "not null"
        text content_styles "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Income Receipt Config and Income Receipt Template models.

Income Receipts

Then each instance of the Income Receipts generated will be tracked individually. It is possible that the Invoice is paid in multiple instalments, and there will be more than one Income Receipt associated with each Invoice.

The system will automatically populate some of the amount fields as such:

  • billable_amount: Copied from the Invoice’s total amount.
  • paid_amount: Starts with 0, each subsequent entry will copy from the previous entry’s payment amount.
  • payment amount: Input by the user.
  • remaining amount: Calculated by billable amount - paid amount - payment amount.

However, the user will have the flexibility to overwrite the amount fields where applicable, for example, if the payment is received through a third party service provider and the Income Receipt is automatically generated by that provider. In this case, there may not be an associated Income Receipt Config or Invoice.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    IncomeReceipts {
        text id PK
        text receipt_number "not null"
        date receipt_date "not null"
        numeric billable_amount "not null"
        numeric paid_amount "not null"
        numeric payment_amount "not null"
        numeric remaining_amount "not null"
        json custom_fields "not null, default: '{}'"
        bool voided "not null, default: false"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text income_receipt_config_id FK
        text invoice_id FK
        text currency_id FK "not null"
        text income_transaction_id FK "not null"
        text contact_id FK "not null"
    }

Income Receipt model.

Expense Receipts

While Income Receipts are created when the user’s business receives payments from a client, and the payment proof is issued in the form of a Receipt, Expense Receipts are received when the user makes payment to external parties. Since the nature of these Receipts are different, we make a distinction in the modelling.

The fields in the Expense Receipt are also simpler, as they only need to mirror the Expense Transaction fields for record keeping purpose.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    ExpenseReceipts {
        text id PK
        text receipt_number "not null"
        date transaction_date "not null"
        numeric amount "not null"
        numeric home_currency_amount "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text currency_id FK "not null"
        text expense_transaction_id FK "not null"
    }

Expense Receipt model.

Sequences

The user may configure the number Sequences to use in the Invoice Templates and Receipt Templates. Prefix and suffix fields are available, so that the user can customise the Invoice number or Receipt number in the Template accordingly.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Sequences {
        text id PK
        text name "not null"
        numeric starting_number "not null, default: 1"
        numeric last_used_number "not null"
        numeric increment_step "not null, default: 1"
        text prefix
        text suffix
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Sequence model.

Contacts and Billing Configs

To simplify the configuration of Invoices and Receipts, the user can create Contacts, which contain important information such as name, address and account number, then use them in the Invoices or Receipts configuration.

The user may configure a unique billing structure for each client, eg. charging by an hourly rate for work done, a fixed cost for a project of various complexities. When generating an Invoice from Work Logs, the Billing Configs will be used to generate the Invoice Lines accordingly and perform the calculation where applicable.

For now, there will be 3 rate types:

  • duration: calculate the sub-total using the Work Log duration.
  • count: calculate the sub-total using the number of Work Log entries.
  • fixed: a fixed value regardless of the number of Work Log entries.

Note that the user can also create her company contact as an entry, and then use it in the System Config.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Contacts {
        text id PK
        text name "not null"
        text account_number
        text address_line_1
        text address_line_2
        text address_line_3
        text city
        text state
        text country
        text postcode
        text contact_number_1
        text contact_number_2
        text contact_number_3
        text contact_person_1
        text contact_person_2
        text contact_person_3
        text contact_email_1
        text contact_email_2
        text contact_email_3
        text url_1
        text url_2
        text url_3
        blob logo
        json custom_fields "not null, default: '{}'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

    BillingConfigs {
        text id PK
        text description "not null"
        date effective_start "not null"
        date effective_end
        text rate_type "not null, default: 'duration'"
        text unit "not null, default: 'hour'"
        numeric unit_cost "not null"
        json include_tags "not null, default: '[]'"
        json exclude_tags "not null, default: '[]'"
        text contact_id FK "not null"
    }

Contact and Billing Config models.

Currencies

The user may need to setup different Currency exchange rates for the purpose of calculating Income or Expense Transactions in another Currency back to her home Currency for tax computation.

Note that the exchange rates are relative to USD for standardisation purpose, which means for the entry for USD, the exchange rate should be stored as 1 (this will be loaded automatically by default at the outset). If the user’s home Currency is not USD, then two step conversion will be applied. For example, if the user’s home Currency is GBP, and the client Currency is JPY, then the system will convert JPY to USD, then from USD to GBP.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Currencies {
        text id PK
        text code "not null"
        text symbol "not null"
        numeric exchange_rate "not null"
        date effective_start "not null"
        date effective_end
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Currency model.

Documents

The user may generate Invoices or upload Receipts, and tag them for categorisation purpose, and later compile them using the Tag filters as proof of documentation for tax filing purpose.

Note that throughout the development of the app, it is possible to change this model to not store the file content directly, but to have a reference link to an external storage, eg. Amazon S3, local file system.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Documents {
        text id PK
        text filename "not null"
        text mime_type "not null"
        blob content "not null"
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Document model.

Invoice Documents / Receipt Documents

For each Document uploaded, the user can associate it to a particular Invoice or Receipt. And each Invoice or Receipt can have zero or more Document associated.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    InvoiceDocuments {
        text id PK
        text description "not null"
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text document_id FK "not null"
        text invoice_id FK "not null"
    }

    IncomeReceiptDocuments {
        text id PK
        text description "not null"
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text document_id FK "not null"
        text income_receipt_id FK "not null"
    }

    ExpenseReceiptDocuments {
        text id PK
        text description "not null"
        json tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text document_id FK "not null"
        text expense_receipt_id FK "not null"
    }

Invoice Document and Receipt Document models.

Recurring Tasks and Recurring Task Steps

There’s a feature to allow the user to setup recurring Transactions, and this can be done by creating Recurring Tasks that detail the schedule (represented in cron format) of the task.

There will be built-in task templates that the user can choose from to setup each Recurring Task. Each task can be composed of one or more step, and each step will be executed according to the defined rank. The smaller the rank number, the earlier they get executed, and in sequence. If there are steps with the same rank, they will be executed in parallel.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    RecurringTasks {
        text id PK
        text description "not null"
        text cron_schedule "not null"
        date effective_start "not null"
        date effective_end
        bool enabled "not null, default: true"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

    RecurringTaskSteps {
        text id PK
        text description "not null"
        numeric rank "not null"
        json params "not null, default: '{}'"
        text task_step_template_id "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text recurring_task_id FK "not null"
    }

Recurring Task and Recurring Task Step models.

Recurring Task and Step Runs

For each time the Recurring Task is run, we will record the run for troubleshooting purpose.

Each step also has its own run record, so that we can easily resume or retry particular steps as a recovery mechanism.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    RecurringTaskRuns {
        text id PK
        timestamp start_time "not null"
        timestamp end_time
        text status "not null, default: 'pending'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text recurring_task_id FK "not null"
    }

    RecurringTaskStepRuns {
        text id PK
        timestamp start_time "not null"
        timestamp end_time
        text status "not null, default: 'pending'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text recurring_task_run_id FK "not null"
        text recurring_task_step_id FK "not null"
    }

Recurring Task Run and Recurring Task Step Run models.

Tax Tables, Tax Tiers and Tax Deductibles

In order to compute the tax payable, the user needs to setup the Tax Table accordingly. As tax rates may change over the years, the user can create multiple instances of Tax Tables for each applicable period.

For each Tax Table, there will be a number of Tax Tiers that specify the income range and tax rate for each tier.

There are also Tax Deductibles related to business expenses that can be added as part of the tax computation.

The include tags and exclude tags allow the user to specify what types of Transactions should be included or excluded from the computation.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    TaxTables {
        text id PK
        text description "not null"
        date effective_start "not null"
        date effective_end
        json include_tags "not null, default: '[]'"
        json exclude_tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

    TaxTiers {
        text id PK
        numeric min_income "not null"
        numeric max_income "not null"
        numeric max_payable_amount "not null"
        numeric rate "not null"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text tax_table_id FK "not null"
    }

    TaxDeductibles {
        text id PK
        text category "not null"
        text description "not null"
        text type "not null"
        numeric rate
        numeric max_deductible_amount
        json include_tags "not null, default: '[]'"
        json exclude_tags "not null, default: '[]'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text tax_table_id FK "not null"
    }

Tax Table, Tax Tier and Tax Deductible models.

Alerts

There’s a feature that allows the user to setup a reminder for each Recurring Task. These will be Alerts that appear in the inbox, and the latest Alert will also be displayed as a banner in the app.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Alerts {
        text id PK
        text title "not null"
        text content "not null"
        bool is_read "not null, default: false"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Alert model.

Chart Configs

The user can configure what charts to display in the analytics page. Each chart has a display order, the lower the number, the earlier it is rendered on the page. If there are multiple charts with the same display order, then they will be rendered on the same row where possible, or will wrap around to the next row if not.

The user can select which data source to use for rendering the chart. There’ll be 2 options for now:

  • Transactions: summation by amount
  • Work Logs: summation by duration
%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    ChartConfigs {
        text id PK
        text description "not null"
        text chart_type "not null, default: 'bar'"
        text data_source "not null"
        text scale "not null, default: 'month'"
        text group_by "not null"
        json include_tags "not null, default: []"
        json exclude_tags "not null, default: []"
        date start_date "not null"
        date end_date "not null"
        numeric display_order "not null, default: 1"
        bool active "not null, default: true"
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

Chart Config model.

System Configs

Finally, the user can setup System Config that applies to the entire app. There can be multiple instances of the System Config, so that the user can change the config at different period in time.

Tags can be formatted using category and name, and for convenience, we will use the %{} syntax to represent variables interpolation. However, we do not make assumption about the templating framework we will use at this stage, and in the end it may turn out that we will use a framework with a different syntax, eg. Liquid template language, and the syntax will change accordingly.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    SystemConfigs {
        text id PK
        date effective_start "not null"
        date effective_end
        text tag_format "default: '%{category}:%{name}'"
        text timezone "default: 'UTC'"
        timestamp created_at "not null"
        timestamp updated_at "not null"
        text base_currency_id FK "not null"
        text base_contact_id FK "not null"
    }

System Config model.

Users

Finally, there’s the Users that we need to store to support multi-user login feature.

At this stage of the design, we don’t want to make too much assumption about the authentication mechanism, so we will start with a password-based authentication. Note that we will encrypt the password before storing, but for simplicity we will just call the field “password” instead of “encryption_password”.

For security reason, we may evolve to using more complex encryption, eg. salted encryption, and then we will need to add a field to store the salt, or we could be using other authentication mechanisms, eg. OAuth, and we can evolve this model accordingly.

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
  "primaryColor": "#f4e3d7",
  "primaryTextColor": "#502d16",
  "primaryBorderColor": "#784421",
  "lineColor": "#784421",
  "secondaryColor": "#a05a2c",
  "tertiaryColor": "#c87137"
}
  }
}%%

erDiagram
    Users {
        text id PK
        text username "not null"
        text password "not null"
        text first_name
        text last_name
        timestamp created_at "not null"
        timestamp updated_at "not null"
    }

User model.

That’s All

If you have read this far, you would have noticed that there’s some business logic hidden in the description of the domain models. That resonates with the statement earlier at the start of this article, where I stated that fitting domain modelling can make implementing business logic more seamless.

Data are the soul of a system, and by looking at the data models, one should be able to guess what sort of system they belong to, and even deduce what kind of logic is implemented in the system.

Note that I used the word “fitting” here rather than “accurate”, because domain modelling is an art as much as it is a science, different engineers may imagine the domain in slightly different ways, and the resulting domain models may look slightly different, so there’s no one accurate answer to the design. However, we can say that there will be certain commonalities among various designs because these are obviously relevant to the domain, eg. Transactions.

That’s about all the domain models that we have identified and designed in details for now. Let’s move on to designing the user journey of the app!