Can you have a clean domain model?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
I was asked a great question by a listener about whether it's always possible to find a good domain model. Sometimes, the business rules are so messy, how can we find something clean and stable? In this episode, I explore how we can find a stable and clean domain model within the chaos.
Eric Normand: Can you have a clean domain model? In this episode, we will explore what can you do with a messy talking, messy set of business rules. My name is Eric Normand, and I help people thrive with functional programming.
I got this great question from Niels Bom. He actually recorded it on audio and emailed it to me. That was pretty cool. The question was whether it was always possible to find a nice clean domain model.
For example, a lot of times we don't really control the business rules. They come from laws, or it's we work at a big company and they have a bunch of regulations and there's no central body controlling whether there is some kind of coherency between all the ideas in there, whether it even makes sense.
Is it possible to come up with a domain model in those kinds of situations where it is just a mess? It's a mess. I think, yes but I think it might require a little bit of deeper understanding of what I mean by domain model.
I'm going to use this story. I worked on in Election Registration System. It was also a notification system. It'd notify you if you had an election coming up. It was supposed to work all across the US. That was the goal of the system, is to have any US person notified of elections that they could vote in.
Increase democracy, more voters is more democracy. That was the theory.
Now, to tell this story, because I know a lot of my listeners are not in the US. The US is a big country divided into 50 states. When you register to vote, you actually register to vote with your state. Every state has different rules, laws, regulations about how to register, who can register, how the elections are run, how to show up for an election.
Do you need ID? All sorts of stuff like that. Where do you go? Every state, all 50 of them, have different rules. Some have better rules than others — like cleaner, easier to follow rules — and some have pretty convoluted rules.
In order to serve people all over the country, we needed to have all those rules in our system. You can imagine we could start with something like a function that takes an election and a person, and tells us whether that person can vote in the election.
It's just like a can-vote function. We just start with the rules, like if you live in this state and the election is in this state, then yes. Otherwise, no.
We have this really hairy, nasty nested if statement. Just a bunch of ifs, just a bunch of conditionals. You look at that and you're like, "OK. I got it working." A rule changes and you pull your hair out because you've got to go in and figure out where to put this new rule or change what rule.
If we take a step back, I actually think the can-vote function is a pretty good start. It boils it down to the essential, like yes/no. There's all these other underlying concepts. This concept of a person being eligible, that is part of the domain model because that's not going to change. The fact that they may or may not be eligible for an election, that is an unchanging thing.
Regardless of the state that they are in, regardless of laws that change, this is something stable that we can relax about. We can put it into our domain model.
What about residency? Is that something? I don't know. I don't know the answer. Maybe this is something that is a universal that a person is a resident of a particular state, or maybe it's a one-to-many relationship. Maybe a person can be a resident in multiple states, depends on the rules of the state.
If there's one state that lets you be resident of it and another state, then we have to make it multiple. A person is a resident of one or more, maybe zero or more. I don't know. We have to do the modeling. I don't know what the answer is, but you can do that. You can just sit there and do that modeling.
Find something that is timeless that works for all of the states. Notice, we haven't implemented the rules like who is eligible for one election. We haven't done that, but we have decided that there is a concept of eligibility, that is like a Boolean. It's like a yes or no.
Maybe we would be even more specific. Maybe it's no. You're not eligible because there are some very clear cases. No. You don't live in that state. You cannot vote in that election. There's definitely some things like that. Maybe it's no and maybe. I've done my best to implement these rules, and maybe you can vote.
That could be the best answer we can give. You might be able to vote in that election. We think you can. We can't find any reason you can't. When you get there, they might have some rule that knew that day. There's also this concept working in the system. I know this domain a little bit.
There's the concept of, "You have to be registered to vote in some states." Some of them say, "You have to register 30 days before the election," or it doesn't count.
"You can't register the day off, and then walk into the booth because we need time to print out the books, the tables with everybody's information in them. You have to register 30 days before."
This idea that you have to capture whether they are registered and what date they registered, that might be a universal thing. Maybe they don't need to register, but it doesn't hurt that it's part of the data model, that they have potentially registered yes or no for the state.
States are divided into districts. If you're a resident of the state, you probably registered in a particular district. That could be part of the data model. Like I said, you'd have to do the modeling, but you can do it. It's not anything more than that.
What I'm saying is, you have to take a step back from the code that when you try to lay it all out at once, it's like this big if statement. It's nested hairy, nasty thing, spaghetti. If you go down deeper, there's a lot of stuff in there that is, for sure, universal. You just have to go out and you got to explore.
You got to look at all the rules, and say, "There's an awful lot of rules about residency." That could be an important concept. What does it mean to be a resident? You dig deeper, and you dig into it into the domain and you see, "Oh, I see. Residency is a one-to-many relationship."
You do it. You just put it. There you go, you modeled it. That's part of your domain model. The business rules can still be a mess, except now you're starting to talk about, "Well, this can vote."
Let's not talk about election and person to Boolean. Let's say it's for a particular election just to make it easier. Can this person vote? It has one argument, a person, and returns true or false. Think of that as a thing, as an abstraction. I don't know how it's implemented.
Just for a moment, let me step back and say, "If I had a magical function that told me this, would this be enough? Would this solve my problem?" Maybe it is. Maybe that's it.
Now you're saying, "This type signature, this function signature is actually part of my domain model. I just have to find a particular implementation of this function signature that matches all the laws and stuff, and then I'm done. This is the signature I'm looking for.
Now that I know the signature, that's something I can use as a higher order function to pass to other things. It's a fixed thing. It's a point that I can rely on as like an interface point. It's another timeless thing."
What I'm trying to show is that by breaking it down, by analyzing it, by learning about the domain and stepping back from the code that actually implements the thing, you can move quite a lot from your business rules down into the domain model. There have to be things that you can rely on in there. There have to be. There might be two domain models.
Let me use this as a way to bring in a related tangent, related similar situation. Aristotle versus Newton. Aristotle had a system of physics. It was a very messy physics compared to what we consider physics today. It had a lot of rules.
It said stuff like heavy stuff sinks to the bottom of the ocean while light stuff stays on top. Big things travel slower than small things. Big things tend to go downhill. It's like big things fall faster than little things. It's all sorts of rules about how stuff works in the world.
For a lot of things, it works. For a lot of things, it doesn't. Those, you could call corner cases. For thousands of years, this was the system of physics that people used. It was mostly like a philosophical exercise. They weren't doing engineering based on this stuff.
They were just trying to find like, "Well, when Aristotle said big things roll downhill, but this thing goes up the hill, or this is a big thing or a heavy thing that floats on water like a boat. How does a boat, which is really heavy, float on the water?"
They would come up with some other reason like some other if statement to show that, yes, it still makes sense. They're fleshing out this huge, big if statement, nested, messy thing, and along comes Newton. What does he do?
He says, "You know what? It's not really size that matters. It's mass. We don't care about the shape or the size and stuff." He broke it down into these little smaller...Pulled things apart into little constituent things.
He made mechanics which has four laws, Newton's laws of motion, where we're all familiar with those, and are just a handful of concepts, force, mass, time, distance, and then how they all relate together in the system.
I've used this before as an example of a really nice domain model that he's made, all these messy rules. Now you have this clean, four-law domain model that you can build those other things on top of. You can see, "Oh, I see. We've got heavy things. That's mass, big things."
What they meant was massive things. The density is the size, mass divided by its size. That's what makes buoyancy. You can start defining all the things that you saw before, all the phenomena that Aristotle was trying to explain, except that it's not really. It's not really all of it because it talks nothing about a lot of the physics. It's just about motion.
Newton also had to define optics, and light, and color. This is a totally different domain model. This has totally different rules. Doesn't interact at all with the mechanics. I guess what I'm saying is if you're seeing a big messy system, it doesn't mean that there's one clean domain model. There could be two. There could be three. There could be four.
You do have to look past trying to unify everything. Newton didn't unify all that stuff. He unified some stuff. He unified the heavens, stars and planets, and stuff that we could see with the stuff that we deal with on earth. He unified the laws and said, "Look, they are obeying the same laws. This is how it could all work."
Mechanics says nothing about light. It says nothing about color, or prisms, or anything like that. It's just in the point masses. Doesn't even say shape, how stuff rolls. Anyway, what I'm trying to get at is you can have multiple different things in there. The other thing I'm trying to get at is that just because the rules are messy, like the Aristotelian rules...
The rules are messy, like the laws that your country or your state has implemented might be messy and not make sense. There's just no clean way to do it besides a bunch of if statements. That doesn't mean that it's not based on something a little bit more timeless underneath.
Just because they have this term that they're using doesn't mean you can't find a better term or a way to define that in a better way and then translate. When Aristotle was talking about heavy things sink to the bottom of the ocean, and light things stay on top, or big things move slow, what does he mean big things move slow?
They don't move slow. We know that now. Big things don't move slow. What does he mean? He probably meant that it takes more energy to increase the velocity of a massive object. That's what he probably meant. You can translate between them a little bit.
The next thing I want to talk about in this because this is a really great question. I want to do a justice. Stratified design can help. I've talked about this thing before where you have this function that can-vote that takes an election and a person and returns a Boolean.
This signature can be timeless, even though any particular implementation depends on the current regulations and laws and stuff. This signature can be timeless. You can start to say, "What can we do with this signature?"
You could have something that lets you compose them. You can build a rule that says, "If the state of the election is Colorado, then the person has to live in Colorado." If they don't live in Colorado, that returns false. If they do live in Colorado, return maybe. Like I said before, return a maybe. It could be a true.
You have another rule that's got the same signature. The election and person returns Boolean. It says, "If they registered, look at the date that they registered and the date of the election. If there's at least 30 days between them, then true. Otherwise, false."
Now you can compose them together. You can make a system to compose up these smaller signatures into bigger ones. What you are really looking for is something better than if statements. The if statement is not a good enough mechanism for composing up a bunch of messy rules.
Maybe something like this, where you can compose them up with ands and ors is. I don't know. You'd have to experiment with it. You now have this freedom because you've made a fixed point in your domain model, something that you are considering — at least for the time being — to be timeless, this function signature.
Now, we can start building on top of that in our business rules.
I hope I've thoroughly answered this question. I don't think it's easy. I actually consider this to be a pretty advanced functional programming skill mostly because you can get by with if statements. You can. You don't have to think about data modeling so much.
If you are having a mess, if you are in a mess right now, if you have a mess of business rules, you probably could use something like this. That's my contention. That's a good sign that you're ready for this.
If you liked this episode, you can find all the past episodes at lispcast.com/podcast. There you'll find audio, video, and text versions of every past episode. You'll also find links to subscribe and please do, because if you liked this one, you'll get more. There will be more.
Also, links to find me on social media so that you can ask me a question, disagree with me, clarify something, I might have said something incorrect. Please, I'd love to know. If you disagree, if you have better sources than I do, if you're smarter than me and I just don't get it. Please, come on. Hit me up.
This is from a question from a person who emailed me the other day, Niels. I'm really glad that I'm getting cool international audience members. This has been my thought on functional programming. My name is Eric Normand. Thank you for listening, and rock on.