Skip to content

Domain Modeling

intermediate2 min read
  • TypeScript

Representing the concepts, rules, and language of the real-world problem in your code, so the software reflects the business rather than just the database.

What you'll understand

  • What a domain model is
  • Why a shared language between code and business matters
  • How domain modeling differs from database design

Explanation

Domain modeling is the practice of representing the real-world problem your software addresses, its concepts, rules, and relationships, directly in your code. The domain is the subject area: for a bookstore it is books, orders, customers, and the rules that govern them. A domain model captures those things and their behaviour, so the code reflects how the business actually works rather than just how data is stored.

A central idea (from domain-driven design) is the ubiquitous language: developers and domain experts agree on the same terms, and those exact terms appear in the code. If the business says "a subscription lapses", there is a lapse concept in the model, not a vague status flag. This shared language removes the constant translation between what experts mean and what the code says, which is where many bugs and misunderstandings hide.

Domain modeling is not the same as database design, and conflating them is a common trap. A database schema is about storing data efficiently in tables (see Related Topics); a domain model is about expressing behaviour and rules. The two are related but shaped by different concerns, an anaemic model that is just database rows with getters has usually let persistence dictate the design. In a layered or clean architecture (see Related Topics), the domain model is the valuable core the rest of the system serves.

Examples

A domain model expresses business rules and behaviour, not just data fields:

class Subscription {
  constructor(
    private status: 'active' | 'lapsed',
    private renewsAt: Date,
  ) {}

  // Behaviour and rules live in the model, in the language of the business
  lapse(): void {
    if (this.status !== 'active') throw new Error('Only active subscriptions can lapse');
    this.status = 'lapsed';
  }
}

Common mistakes

  • Modeling the database schema and calling it the domain model.
  • Building anaemic models: data with no behaviour or rules.
  • Using technical names that the business would not recognise.
  • Scattering business rules across controllers instead of centring them in the model.

Best practices

  • Model the concepts and rules of the business, not just its data.
  • Share one language between code and domain experts, and use it in the code.
  • Keep domain behaviour in the model rather than spread across layers.
  • Let the domain model, not the database, drive the core design (see Related Topics).

Further reading

Related topics