Yet another architecture clean up

This article discusses different approaches to creating a layered architecture – how to reflect the business context in the code; how to structure it; how to understand the dependency inversion and infrastructure layer; and finally as a possibly isolate modules.

SNT collage with text big2Tom Gilb most probably would not call the things I want to talk about architecture. There is not enough space here for all its aspects (estimating, cost, efficiency, engineering). But so be it, let us agree that this is some simplification. And I want to talk about the construction – the structure as an integral element of the architecture.

The purpose of architecture is to implement specific functions in space and use. Uncle Bob says that Architecture is about intent. Speaking of that he juxtaposes the architectural plans of the building with the structure of the folders that collect application code and stresses that intentions often can not be read from the latter. Indeed, the structure of the code often looks like the building depot – one shelf with windows, another with doors, and everywhere else electrical or sanitary equipment. The overall vision, purpose or function is not visible at first glance. This is not however a major problem of software development. The lack of dialogue and involvement of domain business experts are the factors responsible for the software finally not meeting business expectations.

I know it from my mother, who deals with the architecture of buildings, that an investor, who has no clue about architecture, is able to come up with own project drawn on a café napkin. Business knows everything by itself but it does not mean that the expert should not care about the rules of the art.

Architecture is the art of drawing lines

I was amused by the thesis put with such perversity, just like some other listeners of Uncle Bob (see here). Of course, the ability to draw is useful both in the architecture of buildings and in programming itself, but this is not everything. We know well how these drawings look frequently. These rectangles, circles, rollers, arrows and clouds, which we look at seriously and thoughtfully, are often far from reliable architecture.

The ability to think abstractly is really desirable in our business. However, it’s not only about drawing the lines. There is much more to it. It is to outline the boundaries impassable for the objects outside of our interest in a specific context. And it turns out that this thought can be applied to the structure of our code.

Let’s organize our code

Most often we place our components into folders according to their technical meaning. Saying this I mean such situation when all the controllers end up in one place, the views in other, and the services in another. And what if we stop to think this way and try to think of Slices? By Slices I understand grouping of components that work together to achieve a common goal (and probably some of them have no role outside the specified Slice – here in Java package-private scope can be helpful for that). Oliver Gierke, among many others, spoke about this in his interesting presentation Whoops Where Did My Architecture Go.

And now let’s imagine a domain of Classified Ads as an example. Quickly we can realize that we have different contexts, which could be separately developed and implemented, and even that very different people could be involved in it – from developers by product owners to retailers (Bounded Context).

Here’s how the code organization might look like then:

com.example.classifieds.realestate
com.example.classifieds.car
com.example.classifieds.boat

in contrast to the exemplary:

com.example.classifieds.domain
com.example.classifieds.dao
com.example.classifieds.controller

Domain Driven Design introduces the concept of Aggregate – a collection of objects, accessible from the outside via a single object Aggregate Root. So let’s look into the realestate package:

com.example.classifieds.realestate.advertisement
com.example.classifieds.realestate.campaign
com.example.classifieds.realestate.exposure

The intention of such Slices seems to be quite clear. We have three ARs: Advertisement, Campaign and grouping Exposure (single ad campaign). And this is not the place for wondering if by any chance car advertisements will also have their campaign and whether, therefore, they should or should not be moved it out ​​for a more general, abstract level. Now we are talking about the campaign of real estate ads and it lies in this particular context.

Another rule says that communication and the relationship between the units can only be achieved through ARs. Translating it into packages, the objects grouped there should refer to others lying in other packages only through a single point of contact (AR).

Abstraction is intentional

Yes it is, but does that mean that everything that relates, for example, to real estate is located in one place? In case of domain objects: yes, all dependent business objects of such campaign should be in the right package. But if we do not think about a small and simple service, and still think objects including layers, then those layers may lie even in separate packages. Each layer can be imagined in a different place, with its own structure.

As an example let’s look at the application service that assigns real estate advertisements to the campaign. For me, one way of realizing this layer includes Commands, Queries and their Handlers. With this approach I avoid the questions of extracting services – to create them per entity or not; what and how many methods should be grouped in one class of service; according to what criteria we should split them into separate services? When it comes to use cases the Slices do not necessarily coincide with ARs. The approach of Commands and Queries pursues the principle of a single responsibility in an elegant way. Commands and Queries in this approach are nothing but a DTO, and Handlers are Services.

com.example.classifieds.realestate.commands

public interface AssignAdCommandHandler {

void handle(AssignAdCommand command);

}

If we use the maven nomenclature we could even talk about a separate artifact, that the higher layer would depend on, such as :

<groupId>com.example.classifieds</groupId>
<artifactId>ex-cf-re-commands</artifactId>
<packaging>jar</packaging>

You can ask yourself the question – but what for ? Well, just to realize another thing important in the design of managing dependencies. The above interface and other similar to it are a contract, on which depends the higher layer.

More blog articles

It is not enough to invert the arrowheads

People often confuse the concepts of inversion of control, dependency inversion or finally dependency injection.

  • The first one is related to the well-known pattern publish – subscribe and comes down to the Hollywood Principle – do not call us, we’ll call you. The object registers its interest in the occurrence of certain events and is informed thereof.
  • In the second case, ‘inversion’ is more unfortunate, in my opinion. The content of this principle is much more important. In a nutshell it is to find that all the dependencies that are abstract.
  • Finally, dependency injection is ultimately the implementation of the first and promoting the second one, enabling design based on loosely coupled components. An object is provided with the dependencies, it does not know anything about the provider and does not need to care how to get them.

As Uncle Bob said in order to avoid a tight coupling of upper layer objects to the low-level details, it is sufficient to reverse the arrows. Well, let’s draw it for fun.

( orange lines represent layer borders; black arrows the dependencies )

Dependencies direction

Dependencies direction

In my opinion, it is easier to talk about abstraction and externalization of these details than inversion. This inversion in fact boils down to just such a illustration:

Dependency Inversion

Dependency Inversion

The direction of dependencies remains the same except for an abstraction and expulsion introduced beyond the pale of the specific implementation.

Infrastructure is the orbit

Clean Architecture, which combines a variety of related concepts and tries to achieve the purpose of separation of concerns with the use of layers, puts infrastructure as the outermost layer (see here). The figure inferred from the Onion Architecture represents layers in the form of containing circles. This might cause another mistake in thinking. You can in fact think that this outermost layer contains all other layers; that it knows about all the lower ones; and that it depends on the presentation layer and that through it, the outermost layer is able to provide details to any layer below.

Meanwhile, it is easier to think of an infrastructure as of an external orbit, where different details of the various aspects of the application circulate (such as technical details to access the database, frameworks, http services, and so on). These implementations may be completely independent modules supplied even with the aid of the injection mechanism in different places of the system. They also might not represent dependencies in compile time but run time – that is, our code does not refer to them directly. The figure below reflects the nature of what I have in mind.

( orange satellites realize interfaces of certain layers )

Clean Architecture - Infrastructure as Orbit

Clean Architecture – Infrastructure as Orbit

As infrastructure I see here such space which has external libraries. I’m going to continue in this reasoning. Although they are external, none of the layers depends on them directly, it can implement interfaces exposed by any layer, and thus depend on these layers and support them (see here).

Let’s spin the barrel

Let’s imagine a specific implementation of application services, commands from our example potentially delivered to some controllers:

<dependency>
<groupId>com.example.classifieds</groupId>
<artifactId>ex-cf-re-handlers-spring</artifactId>
<scope>runtime</scope>
</dependency>

In my view, such implementation can already be placed in the infrastructure space. The higher layer depends only on the application service interfaces.

Or let’s have recourse to something more obvious. Our domain has Repository interfaces. The actual persistence is outside the domain. Let’s imagine that the implementation uses Spring. The domain is still independent from the framework.

<dependency>
<groupId>com.example.classifieds</groupId>
<artifactId>ex-cf-repositories-spring</artifactId>
<scope>runtime</scope>
</dependency>

Of course, in order to be able to use the dependencies in that way in run time, our implementations would have to use some kind of mechanism allowing to locate them. There are different solutions that can be used for this purpose. Just to mention spring-context, which provides component scanning mechanism and corresponding annotations that allow us to properly mark the candidates for the injection.

Summary

In this article, I wanted to show you some things that I have not mentioned at the start on purpose. I hope that you could get the following:

  • architecture is about abstraction; abstraction is about intent
  • intent should be seen through context and named slice
  • drawing is about boundaries and dependencies
  • technical details of layers should be deferred and externalized

The question of whether and where we want and need to make layers, loosely coupled components or separate modules remains open. An additional complication of code may not always be desirable, and pragmatic justification of abstraction is not always convincing. I know from my own experience that a shortcut often leads to a code which is hard in development and maintenance. Architecture is not a matter of tradeoffs. Of course it is also not about building blocks. However, one should be aware of the existing rules, techniques and feasibility of modular software.

Finally, here you can find the code sample I played with working on that article https://github.com/ameros/yaacu

Further Reading


SNT CollageThe SNT Commercial team delivers Classified Ads solutions for such Norwegian publications as Aftensposten, Stavanger Aftenblad, Bergens Tidende and Fædrelandsvennen. They use Finn.no and Adstate.com as external providers, and SAS, AIDA as internal ones.


 

Subscribe to our newsletter
Menu