Implementing Domain-Driven Design for Microservice Architecture

services such as the order management service, the customer management service, etc.

Services communicate using either synchronous protocols such as HTTP/REST or asynchronous protocols such as AMQP (Advanced Message Queuing Protocol). Services can be developed and deployed independently of one another. Each service has its database to be decoupled from other services. Data consistency between services is maintained using the Saga pattern.

As the services are very granular, client applications usually need to interact with multiple services to get the data they need. To allow changes in the services without affecting the clients, an API Gateway is used. The API Gateway is an abstract layer that hides away all the microservices, leaving a single endpoint for clients to communicate. Requests coming to the gateway will be proxied/routed to the appropriate services. The gateway can also help us to monitor the usage of the services efficiently.

Microservice, a component in this architecture:

  • Each is a miniature application
  • Each is focused on one task, a business capability (The Single Responsibility principle: Each microservice implements only one business responsibility from the bounded domain context. From a software point of view, the system needs to be decomposed into multiple components where each component becomes a microservice. Microservices have to be lightweight, in order to facilitate smaller memory footprints and faster startup times.)
  • Each can be deployed and updated independently
  • They are loosely coupled
  • Each has a well-defined interface: REST APIs

Image for post

FIGURE 4: Microservices Architecture.

Microservices architecture is far more flexible and resilient providing these benefits of using this approach to build an application:

  • Microservices architecture is a very promising way of designing and building highly scalable applications in an agile way. The services themselves are straightforward, focusing on doing only one thing well, so they’re easier to test and ensure higher quality.
  • Each service can be built with the best-suited technologies and tools, allowing polyglot persistence and such. You don’t have to be stuck with a new choice of technology for the rest of the project.
  • Multiple developers and teams can deliver independently under this architecture. This is great for continuous delivery, allowing frequent releases while keeping the rest of the system stable.
  • In case a service goes down, it will only affect the parts that directly depend on it (if there are such parts). The other parts will continue to function well.
  • Externalized configuration: This externalizes the configurations in the config server so that it can be maintained in a hierarchical structure, per environment.
  • Consistent: Services should be written in a consistent style, as per the coding standards and naming convention guidelines…
  • Resilient: Services should handle exceptions arising from technical reasons (connectivity and runtime), and business reasons (invalid inputs) and not crash. Patterns, such as circuit breakers and bulk headers, help isolate and contain failures.
  • Good citizens: Microservices should report their usage statistics, the number of times they are accessed, their average response time, and so on through the JMX API or the HTTP API.
  • Versioned: Microservices may need to support multiple versions for different clients until all clients migrate to higher versions. There should be a clear version strategy in terms of supporting new features and bug fixing…

Microservices architecture layers

The business services tend to do what the domain model and integration layers used to do, and so are still implemented that way. (This is also what the SOA services used to do (or were supposed to).) It’s just that these layers don’t span the whole app, they’re encapsulated within each service. (Yeah, the O/R mapping in the integration layer was more broadly reusable when the layer spanned the whole app. Microservices value independence over reuse. And you should be using No SQL database anyway, so you shouldn’t need all that O/R mapping code. But to tell the truth, some business services will still need to interface with legacy enterprise databases of record, which are SQL, so they’ll need O/R mapping code; there’s no free lunch.)

The dispatchers do what the app model used to do: One stop shopping for the client so that all of the business services together look like a single app that does exactly what the client needs. But now you have an app model for each client type, not a single app model layer that spans the entire app.

What happened to the view layer? It moved into the clients (where it belonged all along!). Either the client is a mobile app, which is a view with perhaps a bit of its app model; or it’s a partner app that’s way more than just a view, but that has its own views, or it’s a web app. Web apps used to contain a lot of view code to render the HTML, and needed state from the view in an HTTP session, but no more. With modern techniques using HTML 5 and CSS 3, the web browser uses static files (downloaded from the web site) to do all that rendering and store the session state.

Image for post

FIGURE 5: Microservices architecture layers.

Backend for frontend (BFF)

Each dispatcher is a back end for an external front end, typically a GUI The same team develops each back end and front-end pair; Use the same or compatible languages in the pair. Usually, rather than have one team write all/multiple dispatchers while someone else implements the clients, instead put the same team in charge of a dispatcher/client pair. The two are going to have to be designed for each other, so have the communication take place within a team, not between teams. The skills tend to be different for different types of clients, so let the teams specialize.*

Image for post

FIGURE 6: Back ends for front ends.

Asynchronous communication

Asynchronous communication can make microservices more robust

  • The requester doesn’t have to block while the provider runs
  • Different requester instance can handle the response
  • Messaging system holds action and result

Consider integrating asynchronously. Synchronous REST is usually easier to get something working, so start with that. Then consider strategically converting some integration points to async, either because of the nature of the request (long-running, runs in the background) or to make the integration more reliable and robust.*

How is async invocation more reliable? With sync, the whole loop — requester, provider, messages — has to keep working though the whole lifetime of the invocation. With async, the invocation is broken into 3–4 parts; if any one fails, the system can probably retry it for you. Heck, because the requester is stateless, the instance that receives the response doesn’t even have to be the instance that sent the request.

Image for post

FIGURE 7: Asynchronous communication.

Microservices intercommunication

Different microservices may be implemented in different languages (now or in the future), so don’t lock yourself into a language-specific integration technology (say, Java sockets, or even CORBA/IIOP). The standard these days is REST, and JSON/REST, so use that. For asynchronous integration, follow an open cloud-friendly standard like AMQP [an open standard transaction message protocol that is significantly lowering the cost of middleware software integrations through interoperable business messaging. It is the ideal protocol for passing business-critical, real-time data across and between organizations and virtual cloud computing transactions environments in a secure manner] or Kafka *. is an open standard transaction message protocol that is significantly lowering the cost of middleware software integrations through interoperable business messaging. It is the ideal protocol for passing business-critical, real-time data across and between organizations and virtual cloud computing transactions environments in a secure manner Try to resist the urge to use other approaches like XML or serialization. That leads to a combinatorial explosion of protocols that each API provider and the consumer has to support.

Other examples of asynchronous protocols:

  • Message Hub
  • MQ Light
  • RabbitMQ

Lightweight protocols

  • REST such as JSON or HTTP
  • Messaging such as Kafka

The aim is complete decoupling, achieved by these methods:

  • Messaging wherever possible
  • Service registry or discovery
  • Load balancing
  • Circuit breaker patterns

We can mix and match sync and async. Usually, a request/response invocation is either all sync or all async, but this shows that a single invocation can be sync in one direction and async in the other.

Image for post

FIGURE 8: Microservices intercommunication.

Conclusion

Our goal with this blog post was to share our thoughts on how to marriage Domain-Driven Design (DDD) with microservice architecture; (DDD) is a software development approach around solving complex domain model; the solution revolves around the business model by connecting the implementation to the core business concepts. The common terminology between the business/domain experts and the development team are domain logic, subdomains, bounded contexts, context maps, domain models, and ubiquitous language as a way to collaborate and improve the application model and resolve any emerging domain-related issues. Our approach was to put some structure around getting to understand the business domain by analyzing the domain, defining the bounded contexts, defining the entities, aggregates, and services and finally identifying the microservices. Microservices offer some unique advantages over traditional architectures, and it provides, scalability, availability, resiliency to name a few; it also a right approach for keeping developers laser-focused as each microservice is a loosely coupled service with a single responsibility principle.