What is the difference between a domain model and business rules?

Business rules are different from your domain model. What goes where? I hope to tease apart this important yet subtle distinction in this episode.

Transcript

Eric Normand: What is the difference between the domain model and the business rules? By the end of this episode, we're going to have a much better idea of this subtle, yet important, distinction.

Hi, my name is Eric Normand. I help people thrive with functional programming. I've mentioned this before in the Onion Architecture — how there are three main layers.

There's the interaction layer, where all the actions take place. This is interactions with the outside world. Inside that, there's a pure functional implementation of the business rules. That's another layer. Inside that, there's the domain layer, the domain model. Things call in, inward, into that inner domain model.

I've had a couple of questions about the difference between the business rules and the domain model. Why does this thing exist in the domain model and not in the business rules? Where do you draw the line?

I'd like to tell a story about why I make this distinction. Because the more I talk to people the more I realize that maybe this is not such a common distinction to make. I'll tell a little story. Here we go. I was working for a company a few years back, maybe five, oh, wow, long time ago. Eight years ago. [laughs] Getting old.

The company did electronic signatures similar to DocuSign where we would take a document, someone would upload a Word document or something, and it'd get sent out to all the people who have to sign it. The people sign it, and then it's completely signed and done. Early on in the company, I was very early in the company's life.

We were trying to make these contracts very legal, legally binding. One of the things we started doing was learning the laws of contracts, especially in Sweden. They did have ideas about moving outside of Sweden, but at that time we were just in Sweden. One of the laws was that if you send someone a contract, you can't take that back. Sending someone a contract is a legally recognized form of agreement.

If it ever got to court, you could say, "Well, they sent this thing to me asking if I wanted to do it and I said yes so they must have wanted to do it, too." It makes sense that there's a law about this. You can't have someone sign something and say, "No, I don't want to sign it." You were the one who sent the thing to sign. We enforced this law in our system.

How it turned out in our system was, we didn't let you cancel once you had sent it. You could cancel before you sent it saying, "Oh, I'll throw this away. I'm going to make a new one." Once you had sent it to the person, there was no way to cancel it. We got complaints. People saying I want to be able to cancel. I need to be able to cancel it. This is an important part of our workflow.

We were like, "The law says you can't cancel it so are you breaking the law? We don't want to help you break the law. That's not our goal as a software company."

What they told us was, "No, I sent the contract." Then, they didn't like some term in the contract so we got one the phone. We agreed over the phone to make some changes. Now I'm trying to send them a new version and I want to cancel the old one just to make sure that no one signs that. That it's clear, that it's done, that we're throwing that one away.

Because there's a new version that we've all agreed to now. I also want to cancel it so it's tracked. That yes, we send two out of three or canceled with a new revision on top. Things like that. We realized that we were interpreting the law in this very strict way as engineers do, as programmers do.

We think when something says you are not allowed to do this or XYZ means sending a contract means you agree to it. You are not allowed to take it back once you send it. We thought that meant that you're not allowed to basically not sign it, but you are. Even the law allows that because you have other forms of communication with people.

There's other things going on. No one is going to contest this. Even if they do, it's very clear what happened. In a court of law, which has a much more narrative way of arguing over what happened and what it means then our computer systems which are like, "Oh, you checked this box or you didn't check this box." The law is separate from our system.

The moral of our story was don't try to enforce the law. That's the moral, but there's a deeper moral which is that, if you squinted you could see that there should have been a separation between the process of how this document works and all the operations that are allowed on it.

Another layer which enforces certain other rules that are probably from some other entity like the government, like the law that can change independently of us. That hints at this separation of, there's the domain of contracts, which is basically a neutral idea of I'm sending you this written document or I'm sending this document to multiple people.

They will all sign it and then when it's signed, it's done. We can't sign it or change it or anything like that once it's all done. There's that layer. Then, there's this other layer, which says, "Oh, if you send it within this time to this time, it's going to expire within six weeks." There's all these other rules that are happening.

Whereas at this bottom layer, we just want to have a notion of a document can expire. That is a possible operation on a document. The rules about when it happens or how it's triggered. If it's through the UI or if it's a time-based thing, all of that is a separate concern. You can tell it's a separate concern because those are things that change much more frequently.

This idea of a contract has a universal aspect to it, timeless aspect to it. Contracts have been used for a long time. The basics of them are well understood. The details are going to change. What I'm saying is we should separate out the domain model, which is how contracts work in an abstract way versus the business rules, which is how they work in our system.

Which has all those details about how many people are allowed on your plan that you can send a contract to. Maybe on your $10 a month tier, you can only send to five people at a time. That's a business rule. That is a clear business rule. Has nothing to do with contracts. It's about how we make money as a business.

I'm trying to separate these two out because what we wound up doing at the company was really defining a really nice set of domain models for these processes that we had that were separate from the business rules. Then, the business rules could change a lot more. We could experiment. We could add new features.

We could do monetization stuff where we limit features to certain things. The model at the bottom was all the same. I'll tell another story. Same company. I'm not sure if this is absolutely correct, but it'll work in the story.

I think we discovered at some point that the law defined, at least in Sweden, it defined the...a contract is between two or more people, which makes sense. Two or more people. They're in agreement. They want to formalize it, put it down in writing. All right. Then, as we expanded and got more clients, more users, there was a use case that was not covered in this.

That use case was that sometimes a business wanted to sign. Not a person, but a business, where they wanted to say like, "We're offering you this credit card and here's the terms. If you would like it, sign here." It wasn't from a particular person.

Likewise, if you were signing up for a bank account or something, you might have to sign a bunch of agreements, but it's just you signing as the person opening the bank account is just one person. There's no one at the bank who is also signing something or if were renting a car, you just sign that I'm renting the car, and I'm waiving the insurance and whatever.

You sign that, but no one else is signing. The owner of the car rental place doesn't sign. It's just one person so that violates the law in a sense because if we encode the law into our contract model, then we can't satisfy this use case, which seems perfectly natural to do.

We had to back out and take out some rules and validation and think about a more general thing, a more general case. Which is, what if a contract is between one or more people? What if a contract is between zero or more people?

We generalize and abstract it because maybe we never have a contract with zero people, but we had to make so many changes when we had this two or more rule. There were a lot of assumptions in the code about there being two. What if we backed out just as a thought experiment all the way to zero and said zero or more people? What would that look like?

You do that and the idea is you're making a more general system, which goes hand and hand with the idea of the data model being actually more general, more abstract, less specific to those cases.

Still does the contract thing just fine, but it's more abstract, less tied into the details of your business, of the rules that you're following today, of the law of today, or you even interpret the particular law. This is what I'm trying to get at with the difference between your business rules and your domain model. If you look at any business, you'd actually see this.

In my book, I use the example of a pizza business. They make pizza. It's a restaurant. The domain of pizza making is pretty stable. It's some bread. You put toppings on it. You bake it. Someone pays money for it. You put it in a box. You deliver it. Whatever you do, it's kind of stable. It's timeless.

The particular business that's making it, that is making the pizza, has a lot of decisions to make. They have a lot of processes that are proprietary. Making a pizza is not proprietary. Here's a really good example. The particular recipe, that is a business rule. It's unique to your business. You make up the recipe and it is yours.

The fact that there is a recipe, the idea of a recipe, everyone's got that. That goes in your domain model. Likewise, a business is going to have to buy the ingredients from somewhere unless they grow it themselves, but that's unlikely. You have to buy flour. You have to make rules about where do we buy the flour from? How often do we buy it? How do we ship it here?

How do we ship to the restaurants? How do we...etc. How do we count every week how much flour we have left so we don't run out? There's all sorts of business processes that go on about that. The fact that you need ingredients, the fact that you need to measure them with something like a unit like kilograms, those kinds of things go in your domain model.

That they have a price. That they come from somewhere and have to get to your store. That kind of stuff is part of the domain model. I think that a lot of value comes from that separation of the two. Because the domain model changes way less frequently. If you can get that right, then the business rules can change faster. You're changing less code. You can iterate faster on your business.

Likewise, if you've got a good domain model, a well-designed domain model, that's a competitive advantage. Not only can you use it in this fast changing layer of your business rules, but it's giving you advantage. Like, "Oh, yeah, we figured out zero or more documents."

Zero or more signatories on this document is actually a more general case and so we can approach this client who wants that for some reason. We are the ones that have figured out all the possible state transitions in this diagram, that goes in our domain model. Then, we can write a custom solution for you. That is a different UI and a different workflow just for your documents.

That is the business advantage of a domain model. I think, maybe this will never happen, but maybe it could. In theory, it could. In the future, this is what programmers can lend to a business, is this idea of being able to make models that more closely reflect the underlying domain, so that when they code the UI, the business rules, the validation, they're using a more powerful construct underneath, and they can iterate quickly with the top.

What I learned and some of the other programmers who were there from the beginning learned was, sure, the team, the business team, business folks are telling us a contract has to have two people. You don't just implement that directly. You think about it. When you say two people, you really mean multiple people.

We probably should at least figure out what it might look like to have zero people. We're just generalizing. That's our job is to think past the short-term feature description that we would get and think more generally and say, "Well, we know as programmers that there's two cases. There's one and there's many." The zero case, I don't know of that even makes sense. There's one and there's many.

If you're saying many, OK, now it's not just two. Once you get past one, it's many. Many means zero or more. You'd have to be more abstract and think a little bit deeper about the systemic quality of your model. In a way that the business people might not be thinking about. All right. I'm glad I got to share this story. I'll just recap just real quick.

In the Onion Architecture, there are three layers. There's the iteration layer. There's the business rules layer. Then, there's the domain model layer. Everything calls inward because it's an onion. The outside is the interaction layer. The inside is the domain model. They're built on top of the thing underneath.

The reason to separate out your business rules from your domain layer is because the business changes way faster than the domain. The domain in theory is something that you take this pizza domain and you can go to another business and start building on top of it even if it runs in a totally different way.

It'll have a totally different business rules layer but that domain model will still be very useful. That's the idea. Because of that, it gives you a tremendous business advantage. You don't have all your code tied up together.

You want to change one thing. The law changes or you want to service this client who needs a different document workflow. You haven't built the workflow in as this spaghetti code. You've separated it out into layers.

Oh, look. All we have to do is remove our business rules layer and our interaction layer. We still have this really core domain that we've hardened. This works really well. We'll build a new business rules layer and a new interaction layer.

We have this code that will work for anybody. That's awesome, right? That is what I mean when I talk about business rules versus domain model. It's subtle. There's choices in there. Sometimes it's a gray line what goes in where but there's a thick part of that layer that's very clear where things should be.

I hope this episode was useful. If you found it useful, you can find all the past episodes at lispcast.com/podcast. There you'll find audio, video and text transcripts of all the past episodes. You'll also find links to subscribe so look into the future. You can get all the episodes you want as they are published. They'll come right to your phone or however you listen.

You can also get them on video, etc. You can subscribe however you want. You'll also find links to find me on social media. This came from some questions I got over email. I'd love to answer your questions too. I love getting into discussions about this stuff.

Obviously these are just my opinions and I don't know everything. I would love to hear your point of view and your knowledge on this because I'd love to develop this more and understand it better.

Thank you so much for listening. This has been my thought on functional programming. My name is Eric Normand and rock on.