We can do better: on developers and domain models

We can do better: on developers and domain models

With every new project, we developers often try hard to avoid previous mistakes, and we do our best to implement new ideas. But sometimes we fail again, the project grows, it starts feeling like a mess and it takes days to get a new team member onboard. Why does this happen? We tend to forget why we’re doing all the coding in the first place: business.

domain model

Domain is fundamental

Domain represents business rules. And we tend to diminish a domain’s importance.
Because we are bad developers.
Because this is something we too often do throughout our entire careers. Since “Hello, world!”, through our first web app and (probably) up to now, we’ve been focusing on the implementation details, learning new technologies (frameworks, libraries, tools). However, we often lose the business knowledge somewhere in between.
We need to store data somewhere and somehow, so database and ORM are really important choices. ORMs (used wisely!) are great if they don’t enforce design. I found Doctrine and Hibernate (these have much in common ;)) friendly from this perspective.

Beginnings of a domain model

Let’s assume crucial infrastructure decisions are behind us. So, now it’s time to implement our first domain model. After removing ORM mapping, our example domain model often looks like this:

It doesn’t feel like a class, more like good old C structure:

In many projects (especially the smaller ones) that’s fine. The application layer handles simple domain well.
So we have our model, it’s time to ask some questions:

Is that Project valid?

No! It’s missing some required data!

So why do we allow invalid state? Ok, how about now?

But the project needs an owner and start date as well!

How could I know that?

You need annotations and assertions which define these rules. And you just don’t know that project.
Business rules do not follow the developers’ logic, they represent business.
So it’s our responsibility to know them. And we love to make it harder using anemic domain models.

Time to get rich

A rich domain model.
Does it look better? It sure does.
I know this is only a simple example reflecting some imaginary Project domain, but it feels real and gives us much more than a data structure.
Why is everything a value object?
Because every meaningful part of a domain deserves its own definition.
When you speak about a decimal value and currency as something related, you probably speak about money. Why not define a domain model for that?
The project’s start and end dates define project duration. It’s not just any duration. Domain shapes interface and defines validation rules. It’s easy to find what I can do, and it’s hard to break it.
Value objects are very important building blocks. They provide contextual meaning, specific context-oriented interface and rules for set of connected values.

OK, I took the bait, should I start a revolution in my project?

It depends.
If we want to (as developers) understand the logic we work with everyday, then the domain-focused approach is a nice way to go.
If our business is complex (and it usually gets complex) I strongly recommend that.
But this is a long journey with steep learning curve and needs full team involvement.

What can I gain?

Readable core business knowledge in your codebase. It simplifies communication with clients, particularly when you need to talk about someone else’s code. Martin Fowler can tell you.
Clearer discussions inside developer teams. Distinct talks about model and technical aspects (implementation details). Sometimes it’s hard to tell if our bugs came from faulty implementation or wrong domain interpretation. And sometimes we don’t even know what we’re discussing.
New features are more enjoyable. They often feel like new, little projects. And developers love new projects, right?
Another step towards better maintainability and extendibility. It’s easier to reflect business changes in application that was built around business concepts.
You can differentiate between essential complexity and accidental complexity more easily. It’s worth estimating how much time we spend on of business changes and how much is just a consequence of our former design decisions. That can lead us to conclusions about technical debt – how to reduce its growth and how to reduce existing debt efficiently.

What can I do to start?

  • Talk about domain and your ideas with other team members – good communication is required
  • Focus code review on business concerns
  • Design meaningful domain models – their interfaces should reflect business actions
  • Start moving business logic back to domain layer
  • Extract complex data types into value objects – with ORM custom types it’s not really hard
  • Remove any infrastructure awareness from your domain-oriented processes
  • Learn about layers and their responsibilities – try to respect them
  • Create new implementations and mark old ones as deprecated – don’t be scared of code duplication, tests and documentation are really important here

Do not hurry.
Think of it as incremental improvement. Incorporate some time for planning and refactoring, start slowly and get comfortable with new practices. New features are a good place to start.

Things to remember

  • business-first code – design models and processes without thinking of your framework or database
  • talk…
    • to the business people – clarify everything
    • to your dev team – verify ideas and domain understanding
  • don’t allow invalid models state
    • fields are immutable by default
    • mutators ensure state validity
  • use abstractions carefully – a bunch of simple interfaces and light abstractions is better than a wonderful class trying to resolve every existing and possible problem
  • exceptions are part of your modelProjectNotFound makes more sense than ORMs NoResultException
  • go further
    • architecture improvements – onion architecture looks interesting, and you’ve already started with its core
    • Domain Driven Design – read about it and try to incorporate your favourite ideas
    • CQS and CQRS – separate mutators and accessors

Make your software better every day

It’s not easy to change some of our coding habits, but the first time you realize how a rich domain just feels right, it’s hard to go back.
It’s just good to look at things from a different perspective sometimes. If concepts such as the one I’ve just described are impossible to implement in your project – how about considering them for your side project? See what the coolest elements are and then try to see how they work with you. The outcome is better software!

Read more:

Do you like this article?