
Domain modeling by Algebraic Data Types (part 1)

Domain modeling by Algebraic Data Types (part 1)

Why domain modeling?


Computer systems differ from the real world. There are fundamental barriers to each system understanding the other. 

Diagram showing the differences in structure between the real world and computer systems

For a long time, there were limited ways to overcome these barriers. Developers often used modeling methods to model the real world, such as UML (Unified Modeling Language) and Agile practices. Essentially, UML captures the developer's understanding of the real world in the computer world. Although teams use Agile to improve productivity and deliver products at every step of their work, this communication is one-way: domain experts let developers understand the business through user stories, and developers create software by designing based on computer systems.

Diagram showing the relationships between domain experts, devs, code and software

If Agile encourages face-to-face communication and rapid iterative delivery of valuable software, then DDD (Domain-Driven Design) further complements the team’s communication methods.


The core ideas of DDD allow developers to understand the business and use programming languages to build a model that is consistent within the minds of domain experts. This allows domain experts, developers and other stakeholders to share the same model, ensuring that they are discussing the same thing and solving the same problem.

Diagram showing the relationships between domain experts, software, developers, code and the shared model

Domain modeling is arguably the most challenging part of DDD. Unlike with traditional database modeling techniques, developers need to have good abstraction capabilities and map domain knowledge to code through appropriate programming techniques. Using ADTs (Algebraic Data Types) is one of the primary techniques of mapping. 



Algebraic Data Types

For a long time, OO (Object Oriented) languages were the go-to choice for domain modeling as OO techniques can be used to abstract domain models. 


For functional programming languages, we usually use algebraic data types to model the business. ADT is a structured type formed from a composite of other types.


While this article uses TypeScript as an example, it is also applicable to other functional programming languages that have static type systems and ADTs.



ADTs in Typescript

Compared with OO, you only need a small amount of grammatical knowledge to start domain modeling with ADTs, which makes them more suitable for domain modeling. The code also becomes a shared model that can be easily understood by domain experts.



Basic Types

Various programming languages already provide simple (primitive) types such as string, boolean, number, etc. However, you still need to combine these types into larger types to map the real world.


In TypeScript, the type keyword is used to create larger types:

type Name = {
  firstName: string
  middleName: string
  lastName: string

Beyond their obvious purpose, type keywords also act as aliases. Don't underestimate this feature! Aliases can help you record domain knowledge in your domain model. Consider the following code:

const timeToFly = 10

Can you understand this code at a glance? Are we flying for 10 seconds? 10 minutes? 10 hours?  Should you check the documentation? No, the code itself should be the documentation! The improved code is as follows:

type Second = number
const timeToFly: Second = 10


The "Or" Type

“Or” is a Union Type, created by the pipe symbol ("|"). Consider the following type:

type Pet = Fish | Bird

Pet is Fish or Bird type. Generally speaking, functional languages have pattern matching capabilities to deal with union types.


The "And" Type

“And” is an Intersection Type, created by the ampersand symbol ("&"). Consider the following type:

type ABC = A & B & C

The ABC type contains all the attributes in the three types A, B, and C.


Types for functions

Functions are no different than other types and can also be defined by the type keyword, for example:

type Add = (a: number, b: number) => number

Add is a function that receives two arguments “a” and “b” of type number, and returns a number.


In the next article, we will describe how to model domains using these basic types.


Keep up with our latest insights