Domain Modeling Made Functional

Notes on the book Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#.

Understanding the domain

A developer’s job is to solve a problem through software.

define a shared model

Business events

The value of the business is created in the process of transforming data.

Bounded Contexts

We therefore need to create a distinction between a “problem space” and a “solution space,” and they must be treated as two different things. To build the solution we will create a model of the problem domain, extracting only the aspects of the domain that are relevant and then re-creating them in our solution space

bounded contexts as autonomous software components

In general, an event used for communication between contexts will not be just a simple signal but will also contain all the data that the downstream components need to process the event.

trust boundaries and validation

The perimeter of a bounded context acts as a “trust boundary.” Anything inside the bounded context will be trusted and valid, while anything outside the bounded context will be untrusted and might be invalid.

The job of the output gate is different. Its job is to ensure that private information doesn’t leak out of the bounded context, both to avoid accidental coupling between contexts and for security reasons.

Contracts between bounded contexts

build a context map with relationships

workflow within a bounded context

The input to a workflow is always the data associated with a command, and the output is always a set of events to communicate to other contexts.

avoid domain events within a bounded context

code structure within a bounded context

use onion architecture ~= clean architecture

keep IO at the edges

Understanding Types

represent a domain with ADTs

Domain modeling with types

Use type systems to capture the domain model accurately and can be read and understood by domain experts.

simple types + sum/product types + functions

functions = workflows

Integrity and Consistency in the Domain

parse, don’t validate

lots of examples…

Capturing Business Rules in the Type System


type​ CustomerEmail = ​  | Unverified ​of​ EmailAddress ​  | Verified ​of​ VerifiedEmailAddress ​// different from normal EmailAddress​

type​ SendPasswordResetEmail = VerifiedEmailAddress -> ...

Making Illegal States Unrepresentable in Our Domain


consistency = atomicity of persistence

Consistency between different context is hard, use eventual consistency:

only update one aggregation per transaction

Modeling Workflow as Pipelines

We’ll create a “pipeline” to represent the business process, which in turn will be built from a series of smaller “pipes.” Each smaller pipe will do one transformation, and then we’ll glue the smaller pipes together to make a bigger pipeline. This style of programming is sometimes called “transformation-oriented programming.”

Following functional programming principles, we’ll ensure each step in the pipeline is designed to be stateless and without side effects, which means each step can be tested and understood independently. Once we have designed the pieces of the pipeline, we’ll just need to implement and assemble them.

Long-running workflow: Sagas

Implement the model

Understand functions

basic functional programming tutorial

Implementation: Composing a Pipelien

Implementation: Working with Errors

Using the result type to make errors explicit. bind and map.


persistence: state that outlives the process that created it Serialization: the process of converting from a domain-specific representation to a representation that can be persisted easily.


#programming #Domain Modeling #Software Engineering #Note #Reading