Reader small image

You're reading from  Get Your Hands Dirty on Clean Architecture - Second Edition

Product typeBook
Published inJul 2023
PublisherPackt
ISBN-139781805128373
Edition2nd Edition
Right arrow
Author (1)
Tom Hombergs
Tom Hombergs
author image
Tom Hombergs

Tom Hombergs is a software engineer by profession and by passion with more than a decade of experience working on many different software projects for many different clients across various industries. In software projects, he takes on the roles of software developer, architect, and coach, with a focus on the Java ecosystem. He has found that writing is the best way to learn, so he likes to dive deep into topics he encounters in his software projects to create texts that give structure to the chaotic world of software development. He regularly writes about software development on his blog and is an occasional speaker at conferences.
Read more about Tom Hombergs

Right arrow

Mapping between Boundaries

In the previous chapters, we’ve discussed the web, application, domain, and persistence layers and what each of those layers contributes to implementing a use case.

We have, however, barely touched on the dreaded and omnipresent topic of mapping between the models of each layer. I bet you’ve had a discussion at some point about whether to use the same model in two layers in order to avoid implementing a mapper.

The argument might have gone something like this:

Pro-mapping developer:

If we don’t map between layers, we have to use the same model in both layers, which means that the layers will be tightly coupled!

Contra-mapping developer:

But if we do map between layers, we produce a lot of boilerplate code, which is overkill for many use cases since they’re only doing CRUD and have the same model across layers anyways!

As is often the case in discussions such as this, there’...

The “No Mapping” strategy

The first strategy is actually not mapping at all.

Figure 9.1 – If the port interfaces use the domain model as the input and output model, we can choose not to map between layers

Figure 9.1 – If the port interfaces use the domain model as the input and output model, we can choose not to map between layers

Figure 9.1 shows the components that are relevant for the Send Money use case from our BuckPal example application.

In the web layer, the web controller calls the SendMoneyUseCase interface to execute the use case. This interface takes an Account object as an argument. This means that both the web and application layers need access to the Account class – both are using the same model.

On the other side of the application, we have the same relationship between the persistence and application layer.

Since all layers use the same model, we don’t need to implement mapping between them.

But what are the consequences of this design?

The web and persistence layers may have special requirements for their models...

The “Two-Way” mapping strategy

A mapping strategy where each layer has its own model is what I call the “Two-Way” mapping strategy, as outlined in Figure 9.2.

Figure 9.2 – With each adapter having its own model, the adapters are responsible for mapping their model into the domain model and back

Figure 9.2 – With each adapter having its own model, the adapters are responsible for mapping their model into the domain model and back

Each layer has its own model, which may have a structure that is completely different from the domain model.

The web layer maps the web model into the input model that is expected by the incoming ports. It also maps domain objects returned by the incoming ports back into the web model.

The persistence layer is responsible for a similar mapping between the domain model, which is used by the outgoing ports, and the persistence model.

Both layers map in two directions, hence the name “Two-Way” mapping.

With each layer having its own model, it can modify its own model without affecting the other layers (as...

The “Full” mapping strategy

Another mapping strategy is what I call the “Full” mapping strategy, as outlined in Figure 9.3.

Figure 9.3 – With each operation requiring its own model, the web adapter and application layer each map their model into the model expected by the operation they want to execute

Figure 9.3 – With each operation requiring its own model, the web adapter and application layer each map their model into the model expected by the operation they want to execute

This mapping strategy introduces a separate input and output model per operation. Instead of using the domain model to communicate across layer boundaries, we use a model specific to each operation, such as SendMoneyCommand, which acts as an input model to the SendMoneyUseCase port in the figure. We can call those models “commands,” “requests,” or similar.

The web layer is responsible for mapping its input into the command object of the application layer. Such a command makes the interface to the application layer very explicit, with little room for interpretation. Each use case has its own...

The “One-Way” mapping strategy

There is yet another mapping strategy with another set of pros and cons: the “One-Way” strategy visualized in Figure 9.4.

Figure 9.4 – With the domain model and the adapter models implementing the same “state” interface, each layer only needs to map objects it receives from other layers one way

Figure 9.4 – With the domain model and the adapter models implementing the same “state” interface, each layer only needs to map objects it receives from other layers one way

In this strategy, the models in all layers implement the same interface, which encapsulates the state of the domain model by providing getter methods on the relevant attributes.

The domain model itself can implement a rich behavior, which we can access from our services within the application layer. If we want to pass a domain object to the outer layers, we can do so without mapping since the domain object implements the state interface expected by the incoming and outgoing ports.

The outer layers can then decide whether they can work with the interface or whether they need to...

When to use which mapping strategy?

This is the million-dollar question, isn’t it?

The answer is the usual, dissatisfying it depends.

Since each mapping strategy has different advantages and disadvantages, we should resist the urge to define a single strategy as a hard-and-fast global rule for the whole code base. This goes against our instincts, as it feels untidy to mix patterns within the same code base. But knowingly choosing a pattern that is not the best pattern for a certain job, just to serve our sense of tidiness, is irresponsible, plain and simple.

Also, as software evolves over time, the strategy that was the best for the job yesterday might not still be the best for the job today. Instead of starting with a fixed mapping strategy and keeping it over time – no matter what – we might start with a simple strategy that allows us to quickly evolve the code and later move to a more complex one that helps us to better decouple the layers.

In order...

How does this help me build maintainable software?

Incoming and outgoing ports act as gatekeepers between the layers of our application. They define how the layers communicate with each other, and how we map models across layers.

With narrow ports in place for each use case, we can choose different mapping strategies for different use cases, and even evolve them over time without affecting other use cases, thus selecting the best strategy for a certain situation at a certain time.

Selecting a different mapping strategy for each use case is harder and requires more communication than simply using the same mapping strategy for all situations, but it will reward the team with a code base that does just what it needs to do and is easier to maintain, as long as the mapping guidelines are known.

Now that we know which components make up our application and how they communicate, we can explore how to assemble a working application out of the different components.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Get Your Hands Dirty on Clean Architecture - Second Edition
Published in: Jul 2023Publisher: PacktISBN-13: 9781805128373
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Tom Hombergs

Tom Hombergs is a software engineer by profession and by passion with more than a decade of experience working on many different software projects for many different clients across various industries. In software projects, he takes on the roles of software developer, architect, and coach, with a focus on the Java ecosystem. He has found that writing is the best way to learn, so he likes to dive deep into topics he encounters in his software projects to create texts that give structure to the chaotic world of software development. He regularly writes about software development on his blog and is an occasional speaker at conferences.
Read more about Tom Hombergs