Post is about how our domain code is structured

It is a complicated process to build a large application destined for many markets. Local conditions may vary in many aspects: legal regulations or other cultural, technological, financial and functional differences. Such difficulties couple application to regional context, where logic is depending on this on many levels. In a well-designed system, the local context is fully separated from the application code. This can be achieved in many ways, but at OLX Europe we decided to apply Clean Architecture and to extract all differences between versions of application to a local platform level— according to the Ports & Adapters architecture. This architecture contains a base system (port) and other systems that communicate with it — adapted to local requirements (adapter). A good example of such port is REST API, where the mobile app, desktop client and the CLI scripts could be proper adapters.

System does not have to be limited to a single architecture. Many of them are complementary — we can use them simultaneously in same components. It may also be convenient to apply different architectures to individual elements of the system. Some features may benefit from using Layered Architecture, other from Event Driven Architecture — everything depends on application requirements. We call such approach Feature-Driven-Architecture where we can try out new solutions for single functionalities without interfering into the system as a whole.

Clean Architecture has been promoted by Uncle Bob as a proper solution to a problem of treating the database as a heart of the application. In the Clean Architecture, the database is an implementation detail, I/O used to exchange data. When writing a business code, you don’t have to worry about a precise data structure and the way data will be stored. Such decisions can be done later, often saving lot of time, because application requirements can change during development.

Clean Architecture diagram

It is a good practice to use CQRS, what means to separate the write-model from the read-model. In short, in such case you use one object to query the system and another one to interact with it (change its status). We have solved it by implementing the Command design pattern which we use in a similar form in two versions: QueryObjects and CommandObjects.

Example:

This is a query, which hides the how an operation is actually performed. More detailed implementation can look as following:

Here you can see a class witch name specifies exactly what it does. This is how we have built all business logic use cases. This is entry point to domain logic from the perspective of platform code. One class is destined to perform only one operation. Information on how the data is delivered is irrelevant at this stage. You can assume that the repository contains a mechanism that will deliver all the valid objects you need.