Modules in the Infrastructure Layer

Thus far, we've been discussing how we structure and organize code in the Domain layer, but we've said almost nothing about the Infrastructure layer. And since we're using Hexagonal Architecture to inverse the dependency between the Domain layer and the Infrastructure layer, we'll need a place where we can put all the implementations of the interfaces defined in the Domain layer. Returning to the example of the billing context, we need a place for the implementations of BillRepository, OrderRepository, and WaybillRepository.

It's clear that they should be placed into the Infrastructure folder, but where? Suppose we decided to use Doctrine ORM to implement the persistence layer. How do we put the Doctrine implementations of our Repositories into the Infrastructure folder? Let's do it directly and see how it looks:

We could leave this as is, but as we saw in the Domain layer, this structure and organization will rot fast and become a mess within a few model iterations. Each time the model grows, it'll probably need even more Infrastructure, and we'll end up mixing different technical concerns such as persistence, messaging, logging, and more. Our first attempt to avoid a tangled mess of Infrastructure implementations is to define a module for each technical concern in the Bounded Context:

   

This looks much better and is a lot more maintainable in the long term than our first attempt. However, our namespaces are lacking some sort of relation to the Ubiquitous Language. Let's consider a variation:

Much better. It matches our Domain Model organization, but inside the Infrastructure layer — plus everything seems easier to find. If you know beforehand that you'll always have a single persistence mechanism, you can stick with this structure and organization. It's rather simple and easy to maintain.

But what about when you have to play with several persistence mechanisms? Nowadays, it's quite common to have a relational persistence mechanism and some kind of shared in-memory persistence like Redis or Riak, or to have some sort of local in-memory implementation to be able to test the code. Let's see how this fits into the actual approach:

We recommend the above. However, all the Repository implementations are living in the same module. This could seem a bit odd when having so many different technologies. In case you find it interesting, you can create an additional module in order to group the related implementations by their underlying technology:

 

This approach is similar to the unit testing organization. However, there are classes, configurations, templates, and so on. that can't be matched with the Domain Model. That's why you may have additional modules inside the Infrastructure one that are related to specific technologies.

Where should you place Doctrine mapping files or Twig templates?

     

As you can see, in order to make Doctrine work, we need an EntityManagerFactory and all the mapping files. We may also include any other Infrastructure objects needed as base classes. Because they're not directly related to our Domain Model, it's better to place these resources in a different module. The same things happen with the Delivery Mechanisms (API, Web, Console Commands, and so on.). In fact, you can be using different PHP frameworks or libraries for each delivery mechanism:

In the previous example, we were using the Laravel Framework for serving the API, the Symfony Console Component as the entry point for the command line, and Silex and Slim for the web delivery mechanism. Regarding the User Interface, you should place it inside each delivery mechanism. However, if there's any chance to share the UI between different delivery mechanisms, you can create a module called UI at the same level as Persistence or Delivery. In general, our suggestion is struggling with how the frameworks tell you to organize your code. Frameworks should obey you, and not the other way around.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset