Functional domain modelling

Most of us have an idea of what functional programming is, and have at least used some of the functional features of our best known programming language such as Lamba or LINQ in .Net.

When I’ve tried to learn more about functional programming, I’ve always felt that something is missing. There is a lot focus on the functions themselves, but less on where to put them in a structure. I’ve asked fellow programmers, but even if they seem very confident in writing functions, they get quite fuzzy when it comes to implementation of a real business problem, and how to structure the code.

Since I’ve used reactjs for many years I have one good example of how to implement a functional code design. But I have troubles applying these principles to the server side, or to see them as a more common approach. So I applied to a workshop at DDDEurope: Lean and functional modelling, with Marcello Duarte. This is what I learned:

You have to start with the programming

To understand the functional design you need to start with the programming. By understanding some of the most fundamental concepts in functional programming it will be easier to grasp the modelling part later. Now, I’ll probably not be the best to explain this after just two days of learning, but I can give you an overview of what you need to dig into. We used Scala for the programming exercises, but I think this is something that you can find in several languages and some of it maybe even in C#.

I found some descriptions with examples in Javascript, which might be easier to grasp for most of us:

Higher order function: “A higher order function is a function that takes a function as an argument, or returns a function. Higher order function is in contrast to first order functions, which don’t take a function as an argument or return a function as output”. Higher order functions.

Composition and currying: “A curried function is a function that takes multiple arguments one at a time. Given a function with 3 parameters, the curried version will take one argument and return a function that takes the next argument, which returns a function that takes the third argument. The last function returns the result of applying the function to all of its arguments.”
Curry and function composition

Functor: “A functor supplies a box with zero or more things inside, and a mapping interface. An array is a good example of a functor, but many other kinds of objects can be mapped over as well, including promises, streams, trees, objects, etc. JavaScript’s built in array and promise objects act like functors.”
Functors and Categories

Monads: “A monad is a way of composing functions that require context in addition to the return value, such as computation, branching, or I/O. Monads type lift, flatten and map so that the types line up for lifting functions a => M(b), making them composable. It’s a mapping from some type a to some type b along with some computational context, hidden in the implementation details of lift, flatten, and map.”
Monads made simple

Abstractions and compositions: “Software solutions should be decomposable into their component parts, and recomposable into new solutions, without changing the internal component implementation details.”
Abstraction and composition

Thanks to Eric Elliot for the descriptions and examples above!

When trying to write clean code there are things that I sometimes stumble on, e.g. keeping the number of parameters down and writing pure functions without side effects. To use a more functional approach is making it easier to write clean code.

Functional modelling

Functional architecture and design is often based on events and event-driven development. Concepts like event sourcing and CQRS can be considered as functional architectures. These architectures can of course be implemented (and is implemented) with any programming language and a wide range of technologies, but functional programming is very well suited for implementing this kind of architecture.

With event-driven development you identify a business flow and let the system reflect that flow. Data might change shape during this flow; for example in a purchasing domain you talk about a price of a product you buy, but in the finance domain the same amount is considered a cost of the same product. It is not just a different status of the product; it is considered a totally different thing.

Event Storming

A common way of identifying the business flow, is to do an event storming. At the event storming business events are identified, and also roles that are involved in firing the events, and the data that is sent. The team can do the event storming by themselves, but if possible, it is good to involve the business. The events, roles and data is written on post-its and put on the wall to illustrate the flow.

During the event storming it is important to avoid too much talk about implementation details (which is hard), and use cases (also hard). Try to focus on how the business work, the flow, and why things are done in a certain way. Look out for the core domain, and try to understand how it contributes to the business. It is important to understand what the business makes money of!

When the flow is defined it is possible to identify the services. One service should reflect one part of the business (domain). The data flows between the services in form of events, and very often the user roles indicate where it is suitable to draw the boundary of the service:

The services can be implemented in many different ways and they have their own databases or data stores. E.g. one is using a SQL database, while the other is using Eventstore and a CQRS approach. Since the services in this example is part of the same deployable unit there are some limitations when it comes to choosing languages and technology. Each service is responsible for its business rules, and don’t know anything about the others. Each service owns its own data.

Separate Responsibilities

In functional programming it is crucial to separate responsibilities into different parts of the code. This has spread to the object oriented systems too, so for many of us this comes very natural. The business logic is considered as the core of the system, and if you change technical implementations around it (database, client api’s etc.), it should be possible to leave the business logic untouched.

The flow throughout the system

The business logic implementation is composed of many independent functions which are called in a fluent way. I like to think of a system in the form of streams where the data flow and via projections and usage of different functions the data take different forms depending on what is should be used for. The flow sometimes temporarily stop waiting for input and is then saved in queues or databases. With event sourcing the flow could be reset and replayed at any time.

As a simple example, think about using LINQ or Lambda for filtering and mapping the the data that flows throughout the system. Then you’ve taken a big step. But try to think beyond that; can your own functions be written and applied to the flow in an even better way? Maybe, if we learn how to use the monads, functors etc. that was described earlier in this text.

Immutability

As an object oriented programmer I have some difficulties to stop thinking in objects with methods and properties. One problem with objects is that the functions often have side-effects, such as reading or modifying a property. Then the outcome of that function might not always be the same, with the same in-data. Side-effects must be avoided to make the functions composable.

The data that shall be modified in a function must be sent in as a variable, be recreated with the changes applied, and then sent back as the result of the function. The data is immutable. For those of us that has worked with reactjs, we’re quite familiar with this.

Conclusion

To fully understand functional programming is not easy. How to design composable functions using higher order functions, functors and monads is quite hard to grasp. On the other hand, functional programming has influenced us a lot already, and given us the thinking of responsibility, events, event sourcing, and event-driven design. Many of us are eager to learn how to use the reduce function in Javascript, how to implement reactjs in a better way, or writing LINQ and Lambda in C# and Java. All that work takes us closer to the paradigm shift toward functional programming.

Why Microservices?

Microservices is a way of separating a system into smaller autonomous parts. This can be done in many ways, but I’m most into the event-driven architecture and think that is at least a good start.

Every system I’ve been working with has started as a monolith. Some of them written in old languages like Cobol or Delphi, but some of them also in C#. What in a business that drives rewrite of a system is a separate discussion, but if these systems had been modularised in any way, it would have been so much easier (and cheaper) to step-by-step modernize them.

To be able to deal with technology changes of a system is a good reason to use a microservice architecture. To be able to rewrite business logic without affecting the whole system is another.

The possibility to deploy each service separately, has not made sense practically to me yet. Of course it depends on what type of system you’re working with. I think that to make sure that the services communicate using events (e.g. with pub-sub) and have separate data sources is much more important. This makes it easier to split the system if or when it is needed.

Event-driven architecture

In an event-driven architecture, business events are identified e.g. using event storming. A business event can be something like when a booking is done or cancelled, or a hotel is available for booking. When a business event is published other subscribing services are triggered and performing their tasks.

The publishing service don’t know anything more than that e.g. a booking is done. And the subscribing service, let’s say an accounting service, has no idea of how a booking is made, only that it is done and it is time to perform the accounting tasks that it is responsible for.

An event-driven system is loosely coupled and asynchronous. The different services does not now anything about each others, and have no dependencies. In this way logic is isolated and has clear responsibilities. They can easily be deployed and scaled separately.

Read more about event-driven architecture and event storming.

CQRS and Event sourcing

CQRS and event sourcing are ways of taking event-driven design further and think of, and store, every performed task in the system as an event. All except the read of the CRUD operations are saved as created, updated and deleted events. Of course business events are saved as events as well. You get the traceability of everything that has happened in the system. You also get the possibility to generate and regenerate read data in different forms depending on what’s needed.

Microservices is the way forward

Microservices tend to decrease complexity in systems when performing changes in each service. When you look at the whole, though, it might increase complexity. Especially if you’re going to deploy them separately. The infrastructure gets more complex and, for example, if you have different languages in the services, the developers need a broad range of skills.

I suggest that you try to think in generations if you have services in different technologies. Try to keep the number of different technologies down. Develop one bunch of services that are very similar to each other when it comes to structure and technology. When that technology is getting less popular or starts to feel outdated, begin a new generation of services with a new set of technology choices and continue with these for the next period of time. Maybe rewrite some of the old ones, if needed. And continue like this over the years.

From my perspective, microservices (either compiled together or completely separated) is the only way of making a system survive over time. Even the most modern, high tech technology eventually gets old-fashioned. If we don’t want to completely rewrite the whole system every 20th year, we have to split them into smaller units.

One benefit of continuously rewrite the parts of a system instead of rewrite it all at once is that it is easier to attract developers who tends to choose jobs where they get the possibility to work with new technology. I’m sure it is easier to find a python developer rather than a COBOL developer today, for example.

Want to read more? Start here.

What is business logic?

I found it rather common that developers have only a vague idea of what business logic is. I also sometimes find it hard to describe it myself, even though I’ve spent many hours of my career working with it.

So let’s try to clarify things. Business logic is core logic, that doesn’t have anything to do with technology. It is the logic that can be found as rules in the business even though there were no digital system implemented. What is considered as core to a business, is the activities that they gain money of.

Strictly speaking, business rules are rules or procedures that make or save the business money

Uncle Bob

Let’s take the example of a booking that gets cancelled. Business logic are the rules that kicks in with freeing up resources (hotels, cars or whatever is booked), confirm cancellation to customer, and refund money. Technically how this is done; by updating a database, which technology that is used to send an email or which payment solution that is called to refund the money, is not business logic. The technology should be exchangeable.

Business logic should be kept separate from the other logic in the system and it is worth a considerable effort to have a lot of automated testing here to verify that the rules are not broken.

Business rules vs. Use case

Business logic is often divided into two different types: Business rules and Use cases. First, I will address that there might be some naming confusion here, when it comes to business rule vs. business logic. The business rule is something that exists in the business even without a computer, while the business logic is the code that implements the rule.

..the application of VAT on invoices is a business rule but the calculations involved in applying it are implemented as business logic

Ben Morris

When implementing business rules, it is very common that it is some kind of flow in which the rules are called. This flow can be implemented in something called Use case. Other names might be Service or Domain service.

Use cases are easily mapped to the requirements which are preferably defined as test cases. If implementing automated acceptance tests following the test cases you get a good assurance that the functionality that you have agreed with the customer or business is working.

Implementation

Use cases are classes containing the flow for a use case. They can call repositories to save data, or they might call the domain model classes that performs the business rules.

E.g. when a booking is cancelled the hotel rooms should be made available to other bookings. It is the responsibility of the use case to make sure that this is included in the flow, but it is the domain model’s responsibility to know how a hotel is marked as available (maybe by changing the status).

As I wrote earlier, there is much value in automated testing of business logic to make sure that these rules work according to spec. Write acceptance tests testing the Use cases, and write unit tests that tests the business rules:

As we know reality is often a lot more complex that these simple examples. But this can be used as a starting point from which we continuously search for more knowledge. Why not continue by reading this blogpost by Uncle Bob?