In a domain-centric architecture, the domain model is placed at the center of the architecture, while a database-centric architecture is centered around the data model.
The domain model represents the users’ mental model, and the use cases solve their problems. The domain is at the core, surrounded by the application layer. The presentation and persistence layers are details, and all dependencies point to the domain.
The layers are partitions of the application that represent different levels of abstraction, supporting the Single-responsibility Principle (SRP) and allowing the application to be divided into units of manageable complexity.
This modern architecture is divided into four layers:
- Presentation: user interface with the application;
- Application: contains the use cases;
- Domain: contains only the domain logic;
- Infrastructure: interfaces with the database and other infrastructures.
In this architecture, dependencies are reversed and dependency inversion principle applies. Abstraction should not depend on details, but the other way around.
Context boundaries play a key role in recognising a specific contextual scope with a specific domain. This architecture fits perfectly for building and delimiting such contexts with well-defined interfaces, and consequently subdividing large domain models – partitioning large applications into independent, autonomous, highly cohesive, and loosely coupled units that communicate securely and scalably.
I'm referring to microservices. By definition, a microservice is a highly cohesive, loosely coupled, autonomous, and independent component representing a business context of an application.
But who better than Martin Fowler to talk about microservices?
FROM THEORY TO PRACTICE
Now let's put it into practice a domain-centric architecture for consuming a web service. And what service is that? It’s a service dedicated to facts about cats.
There are several examples online about domain-centric architecture and microservices, but I will exemplify it here with consuming a web service. This web service provides facts about cats (available here). The solution is developed in .Net Core.
The domain is CatFact. In the application layer, we focus on the system's use cases. However, in this case, we will keep the number of layers as minimal as possible, since we only intend to query cat facts without additional business logic.
I will exemplify how to invoke a web API using Uncle Bob's Clean Architecture. We use a wrapper to encapsulate the web service calls and return the responses.
Create a new solution with domain-oriented layers:
- Domain Folder
- Infrastructure Folder
To the Domain, add a library project with Core:
- Entities Folder
- Interfaces Folder
To the Infrastructure, add a library project named “HttpClientServices” with CatFacts’ HTTP Client:
- CatFacts Folder
The Interfaces folder contains the interfaces intrinsic to CatFactServices.
Once the entities defining the domain entities are created, and the Core project is referenced by the HttpClientServices library, define the HttpClientWrapper and CatFactService.
Create a Console project to test the web API client.
And that's it!
But we can bring in a layer of logical services and expose our own web API. In this layer, the service acts as a wrapper for the application and provides functionalities through an API that the UI can interact with.
Therefore, the service layer is divided into two physical components: the client and the server.
Let's try the ReactJS HTTP services (client) and the ASP.NET Core controllers (server).
The ReactJS services and the ASP.NET Core controllers are highly cohesive. The methods of these services communicate directly with the endpoints of the ASP.NET Core controllers.
Add an ASP.NET Core Web Application project named “WebApp” with the default template.
Define the CatFactController.
Execute the web API and view the swagger documentation page.
Now, let's try to explore cat facts with ReactJS, but using the previously built WebApp as a pretext.
The React client has the basic structure:
Apply the following sequence of commands in the clientapp directory to set up a basic React application:
- npm init
- npm install react react-dom
- npm i --save-dev webpack webpack-dev-server webpack-cli
- npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react html-webpack-plugin
Configuration, configuration, and more configuration for React rendering:
index.html with CSS for styling
index.js with the React PoC (Proof of Concept)
The command “npm run start” runs the React project and loads the application in the browser.
If there is a CORS error, the CORS Unblock extension can help solve the issue. But remember: only use it during the application’s development process.
The solution used as an example is available on GitHub.