Domain Model

This post explores creating a domain model in F#. It continues the series functional domain project which targets creating a basic asset management API.

Domain Requirements

These are the requirements (I’ve made up) for our asset management system.

  • An asset is a thing that the organization owns that has some capital value
  • An asset may contain another asset (e.g. a park contains a playground that contains a slide)
  • An asset is based on a template to ensure similar assets store the same information
  • A template contains custom fields which have a type (text, numeric, date)
  • An asset stores the values for the fields in its template
  • An asset may have a maintenance program. This specifies what work is carried out at certain intervals. For these requirements, an asset may only have a single maintenance program.
  • Any maintenance carried out must be recorded.

First Model

The following is a first pass at a model expressing these requirements.

module DomainTypes

/// MaintenanceProgram must be immutable in data store so that maintenance records referencing it have accurate information.
type MaintenanceProgram = {
    Id: System.Guid
    Summary: string
    Period: System.TimeSpan
    Details: string
}

type MaintenanceRecord = {
    Id: System.Guid
    // One of MaintenanceProgramId and Summary must be set
    MaintenanceProgramId: System.Guid option
    Summary: string option
    DateComplete: System.DateTime
    Details: string
    Cost: decimal
}

type FieldValue = 
    | StringField of string
    | DateField of System.DateTime
    | NumericField of float

type FieldDefinition = {
    Id: System.Guid
    Name: string
    Field: FieldValue
}

type Field = {
    Definition: FieldDefinition
    Value: FieldValue
}

type Template = {
    Id: System.Guid
    Name: string
    Fields: FieldDefinition list
    // I considered making MaintenanceProgram compositional, but that
    // might be limiting for organizations that want a simple set
    // of scheduled maintenance periods without specifics on the type of maintenance
    MaintenanceProgramId: System.Guid
}

type Asset = {
    Id: System.Guid
    Name: string
    Commissioned: System.DateTime
    Cost: decimal
    Fields: Field list
    TemplateId: System.Guid 
    Subassets: Asset list
}

F# records and discriminated unions provide a very natural way to express a domain model. In this model the majority of information is stored as records, with discriminated unions used to indicate and control the type of value stored in a field.

This model also makes the difference between composition and reference relationships clear. Composition implies the target instance will die with the source instance, whereas in a reference relationship the lifetime of the target is independent of the source. The Asset type illustrates this: the field values make no sense if there is no asset so they are compositional, and therefore we include the Field type within the Fields property of the Asset; whereas the template will be used by many assets so it is referenced by id.

Interestingly (at least to me), one could argue a sub-asset, i.e. a component of a system, could be reused in another asset when its current parent asset is decommissioned. This model is forbidding this by including subassets in the Asset rather than referencing them.

Refining Intent

At this point I wanted to enforce my constraint on the MaintenanceRecord, and via google, ran into the excellent series on F# for fun and profit, Designing with types

Firstly, several fields represent the same concept, but there is no way of identifying that. If the field type is replaced by a single case union type then that relationship becomes clearer. For instance:

type MaintenanceSummary = MaintenanceSummary of string

type MaintenanceProgram = {
    Summary: MaintenanceSummary
    ...
}

type MaintenanceRecord = {
    Summary = MaintenanceSummary option
    ...
}

Next, as noted in comments, we want to ensure one of MaintenanceProgramId and Summary is set in MaintenanceRecord. We can use a discriminated union to enforce this by replacing

type MaintenanceRecord = {
    MaintenanceProgramId: System.Guid option
    Summary: MaintenanceSummary option
    ...
}

with

type MaintenanceRecordSummary = 
    | MaintenanceProgramId of System.Guid
    | Summary of MaintenanceSummary

type MaintenanceRecord = {
    Summary: MaintenanceRecordSummary
    ....
}

This is as far as I’m going to take it for now as my overall knowledge of F# is still pretty limited, but I expect that as I implement the surrounding layers I’ll better understand the purpose of more of the advice within designing with types likely implement them.

Considering Architecture

I’m getting through Expert F#, and was starting to consider how a domain model might be implemented. But I think before that it is important to consider where that model fits into the solution, which requires us to think about architecture.

I must say that my disorganized thoughts were put into a much clearer perspective by the F# for fun and profit post Organizing modules in a project and you will likely find that a more practical and clearer explanation than I’m about to make.

Architecture in general

Any solution beyond a few hundred lines of code needs an architecture to organize the code. An architecture provides hard rules and soft guidelines on how different parts of a solution should interact, with the primary goal of making the solution maintainable. A maintainable solution is one that is understandable, and therefore easier to work on and more secure; is slower to accumulate debt; and is more testable and therefore of higher quality.

An architecture creates organization by separating unrelated concerns, controlling message paths, and hiding information. The goal of this project is to create an API for an asset management solution so we are implementing a web service, and the most common organization for a web service architecture is to split the solution into layers. The previous post, ASP.NET Core with F#, briefly considered an implementation for the top layer – the web interface. Beyond that we must also consider:

  • domain – the business rules and (for an API) the core business value;
  • persistence – how data is stored and retrieved;
  • infrastructure – cross-cutting concerns such as user context, configuration, logging.

In the most naive implementation, this creates the following architecture, where each layer depends on the one above:

archstack1

Domain Layer

I believe an architecture should be designed from the domain outwards because the domain is where the core business value resides. The domain comprises groups of data, which I will refer to as data objects, and domain operations which create or act upon the data. In an OO domain model the relevant operations are stored with the relevant data objects, although this tends to get messy.

Coming from C#, F# adds a new constraint: that types must be declared before they are used. This is not as different as it first seems since a domain focused solution (even in an OO stack) should define its data and behavior separate from its implementation to allow for unit tests (not to mention all the other benefits of clear architectural layers). In C# this means defining domain dependencies as interfaces, then injecting the concrete implementations once they are defined.

As F# is strongly typed, in order to have unit tests we must be able to provide at least two different objects (the concrete and a mock) that share a type. The end result is identical to C#: we must create interfaces to express behavior.

So the domain operations depend on:
1. data objects;
2. interfaces defining operations/services that must be provided. Some of those behaviors will be other domain operations and for the sake of consistency it might be desirable to include all the domain operation interfaces in this dependency.

Other Layers

In defining operations/services interfaces, the domain layer has essentially created the persistence and infrastructure interfaces.

With the domain nicely isolated, the remainder of the architecture must now provide what it needs. We expect that the concrete classes for these dependencies will be largely in the persistence and a little in the infrastructure. This results in a network as follows, with the addition of a startup layer which creates key context classes, such as the dependency injector.

archnetwork2

At this point nothing here is F# specific – and that is a good thing! A good architecture should allow implementation decisions to be made as late as practical.

The end result is we now have a place for our domain data objects and operations, and we can begin to define those.

Functional Domain Project Intro

The first project I plan to undertake is creating an architecture with a functional domain model. By ‘functional’ I mean using the functional programming paradigm, i.e. focusing on expressions rather than instructions.

The (long) rationale

In my career, where there has been an architecture at all, it has tended to be some form of object oriented domain model. In such a model each domain concept (I’m avoiding saying entity due to it’s Domain Driven Design implications) is represented by a class that exposes all its properties, a collection of get/find methods, save method, and some advanced manipulation that reflects more complex business rules. Eventually someone realizes that a given class is getting too complex and creates a separate service for that behavior. And so we end up with a set of heavily intertwined classes representing some combination of data and operations that is appropriate to the business domain.

I find this approach troubling for many reasons.

Firstly, regardless of the original intentions of the architect, these classes always end up getting big. By big I mean that the combination of possible states of the class’s fields makes it hard to reason about what will happen when a change is made in the class. This means that developers are left relying on unit tests (should they even cover the area of concern) to ensure the integrity of changes. I’m not disputing the value of tests, but I believe that better outcomes can be achieved by understanding the code and therefore being able to simplify it, rather that just painting on another method. Simplification means less code and that is better for maintenance, while code that can’t be understood can’t be maintained and is therefore technical debt.
Basically the class fields start to exhibit all the failings of global variables.

Secondly, it isn’t cohesive. A class with high cohesion is one with as few responsibilities as possible. These classes are responsible for reading data, writing data, and performing many different operations, which makes them very non-cohesive. It strikes a nerve with me that people are happy to violate cohesion by putting reading and writing code into the same class when they are such different operations: writing deals with validation, consistency, error handling; while reading deals with filtering, paging, caching; and beyond that we still have business rules and operations that act across classes.
So I believe that reading and writing classes should be separate, despite the vitriolic sentiments of the OO community calling this approach ‘anemic’.

A third issue is one I mentioned earlier: when should operations be moved from the concept classes into their own one? There simply isn’t a good line, and this is a problem because although it might seem obvious to the architect by virtue of their experience, it won’t to an intermediate developer, and so the code will go into the concept class it make it more unwieldy and less cohesive. Additionally, if the operation is moved into another class, can it access all the data it needs, or is that hidden within concept class?

Finally there is testability. Establishing all the states of the object necessary to thoroughly test is hard, especially when some of the fields are private.

I’m told it’s important to break-up lots of text to help people pause and regroup while reading, so here is a random picture of a plane that I took years ago.
p1000491

So what’s my suggestion?

If we take the concept class and split the reading and writing functionality into two classes, then all the data needs to be accessible to both classes, necessitating the creation of a data class, a.k.a. the DTO.

At this point I imagine OO proponents yelling “information hiding” at me (largely because some have), but the reality of the systems I’ve worked with is there is seldom any information that is hidden (let alone needs to be hidden) behind complex methods, and where the data needs manipulation to match the repository format, simple property accessors are fine.

Given we are in the world of web development and objects are usually created for a specific request, there seems very little value in keeping state because it probably won’t change anyway. This raises the question: why allow the state to be changed at all? If the state is probably going to be fixed from the moment the object is created, either from data submitted by the user or via repository access, then let’s enforce that by making the state immutable. Now we get all the benefits of immutability, like thread safety, and a clear ability to understand the object because anytime we perform an operation to it or with it we will get the same result.

The end result is a functional domain model, where data is held in an immutable object, and operations are expressed by functions acting on that data.

Implementation

Given my background is with .NET I’ve decided to do this using F#. To that end I’m presently making my way through Expert F# 4.0 by Syme, Granicz and Cisternino. I’m also hoping to using ASP.NET Core for this, to become more familiar with that.

I’m going to create a basic asset management system to illustrate this. One of the products on worked on around 9 years ago was an asset management system, so the domain for this will draw on what I can vaguely remember from then. The next post will introduce this problem domain.

It will likely be some time before I’m ready to tackle the meat of the project, but before then I’ll try to go into more detail on the problem domain, and get setup with ASP.NET Core in F#.