Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Domain-Driven Design Meets Hexagonal Architecture: A Guide to Future-Proof Applications

Sign upSign InSign upSign InKalpa SenanayakeFollowLevel Up Coding--ListenShareA timeless goal of software engineering has been to separate code that changes frequently from stable code — James Copline /Lean Architecture.The inevitable reality of our software applications is that they will change for many reasons over time. There exists a part of the software that represents core real-world Business functionalities for our organisation that changes less frequently, and there are parts that frequently change.A common challenge for product engineering teams is understanding our product’s core domain. Some teams have business-oriented products, and some are enabler products that indirectly help the business products. In any business, there are domain experts who understand the core business functionalities well. Product engineering teams can leverage these experts and work with them collaboratively to understand the business domain and establish a common language. There are many approaches to establishing that understanding. One of the ways to understand product problem space is to use Domain-Driven-Design and Event Storming.Another challenge is keeping the core domain away from the other parts of the software. The article explains one approach to do that, however There may be many different ways to do this. This approach is battle-tested with proven results to build quality, maintainable software. It is called Hexagonal Architecture. This article will dive deep into it and show a practical use of Driven Design with Hexagonal Architecture.This article introduces Domain-Driven-Design and Hexagonal architecture and in-depth practical guidance to combine both patterns to address the above challenges and build maintainable software applications.Domain-driven design is an approach to software development that centers on programming a domain model with a rich understanding of the processes and rules of the domain.Domain-driven design is about aligning software models closely with the business domain to solve real-world problems effectively. It promotes collaboration between technical and domain experts to ensure software meets business needs.The following are the main building blocks of Domain Driven Design.The Ubiquitous Language: A common language developed by developers and domain experts to ensure that domain concepts are consistently and unambiguously understood across the team. Ubiquitous language is by far the most crucial aspect of DDD.Domain experts have their language and terminology to explain business entities and processes. They use these language constructs to communicate business requirements, but software engineers usually ignore these and model the business requirements into interfaces, classes, and functions. During the translation process, ambiguity and uncertainty sneakers into the software. Using common language between engineers and domain experts is the way to solve this problem.Domain Logic: The collection of specialised knowledge and rules that dictate how a specific business domain operates and evolves.Domain Model: An abstraction representing the core concepts, logic, and processes within a specific business domain, often expressed as a combination of entities, value objects, aggregates, and services.Subdomain: A smaller, more focused area within a larger domain dealing with a particular aspect or function of the business. For example, “billing” might be a subdomain within a larger “payment” domain.Bounded Context: A clear boundary within which a specific domain model is defined and applicable. Within this context, terms and concepts have explicit meanings, preventing ambiguities that can arise in large systems.Entities: Objects with a distinct identity and a lifecycle within the domain model. Their identity remains consistent even if their attributes change.Value Objects and Aggregates: Value objects are immutable objects defined only by their attributes and do not possess a distinct identity. Aggregates are clusters of entities and value objects treated as a single unit for data changes, ensuring consistency.Domain Service: Operations, actions, or logic that don’t naturally belong to an entity or value object but are essential within the domain. These services encapsulate domain logic that doesn’t fit within the context of a single entity or value object.Repository: A pattern that provides an abstraction layer for accessing and persisting aggregates, making it easier to retrieve or save domain objects without dealing directly with data storage concerns.One tool offered by domain-driven design is Event storm. Event Storm is a collaborative session with domain experts, engineers, product owners, business analysts, etc. This session aims to understand the business process as a holistic entity. It is a rapid, lightweight, and visual domain exploration method.Imagine that you have time-traveled to 1900, and you and your team are running a retail department store company with a brick-and-mortar store, and all your ledgers are physical files. Computers or information technology will not be invented until another 40–45 years.This mindset will help you avoid client-server technology, data formats, databases, cloud computing, single-page-web-applications, mobile applications, and everything you know about running a business in 21 century.The purpose is to understand how this department store works with its various business functions working with one another.Domain Events: These are the indicators of something that has happened within our department store related to the business process.Aggregates: In Domain Driven Design, it’s a cluster of domain objects that can be treated as a single unit.Commands: They represent a request to perform an action or change from a customer or store manager.Read Models: These are physical books in our 20th-century department store.Bounded Contexts: These define the limits within which a particular model is defined and applicable.Ubiquitous Language: The common language that evolves between business experts and developers. It’s essential that an “Order” in the sales context means the same thing to an engineer and a store manager.Even though this example is simple, it has enough complexity to convey the idea behind using Event Storm to understand the business process.Now we understand the business process via event storm and domain-driven design terminology; it is time to dive into other pillars of this bridge.Before diving into Hexagonal Architecture, let’s start with the well-known layered architecture and ask why we need another architecture pattern.The above diagram shows the traditional layered architecture, which we all know. The idea behind that approach is that the system is built of layers stacked on each other.Each layer has a distinct responsibility. This separation facilitates modularity, scalability, and maintainability, and the idea is to swap or upgrade layers independently when the changes are required.While this is getting faster results for product engineering and that business team, there is a ticking time bomb hiding in plain sight.Let’s have a look at a real-world example. We have a microservice with internal architecture constructed using layered architectural principles.Initially, when the business requirements are simple, and the application itself is relatively simple.After a few years, the payment service requires many other services to fulfill its tasks. Along the way, many things have been changed; the team delivered several critical features on top of the original payment application. Now, it’s not the simple, innocent application we used to have.One could argue that this does not have to be like this; basic SOLID principles can significantly improve this code. That is true, and this does not have to look like this. At the same time, one could be surprised at how many real-world application codes are like this.This code snippet is trying to convey that over time, simple becomes complex, and the layered architecture approach cannot deal with that while protecting the domain logic.If we look at the dependency graph for the payment service, it will look like below.Imagine changing to “PaymentService.” The IDE light-up complaints changes from many of these dependencies when we touch the code.And once we fixed all that, the test cases were unhappy, so we fixed those, pushed the code, and deployed.But, during the smoke test, we may find that wallets are not updating correctly. Since there was no mention of the changes related to the wallet, we have not considered changing or testing the wallet. However, there were scenarios where the wallet service interacted with the payment service inversely. We did not know we had to update the wallet service logic.If that sounds familiar, then you have already experienced the problem with layered architecture and its shortcomings in developing software for complex business processes.The problem is that when the world around the business logic changes, the level of abstraction provided by the layered architecture is not capable enough to protect the critical business logic from the external world.Over time, essential business logic mixed with concepts like databases, feature flags, validation, etc, makes it hard to change.This why we need another architecture.Hexagonal architecture can help you to solve the problems packaged with layer architecture. We are going to take a look step by step.The idea of Hexagonal Architecture is to put inputs and outputs at the edges of our design. Business logic should not depend on whether we expose a REST or a GraphQL API, and it should not depend on where we get data from — a database, a microservice API exposed via gRPC or REST, or just a simple CSV file.The pattern allows us to isolate the core logic of our application from outside concerns. Having our core logic isolated means we can easily change data source details without a significant impact or major code rewrites to the codebase.— Nexflix Tech BlogNow that we know what is inside Vs outside, let’s zoom in briefly.A bit more zoom-inAt this level of zoom, we can see that keeping the domain entities protected from the rest of the application is the most crucial aspect of the Hexagonal Architecture, and it shows how to do that using contracts between Domain logic (Ports) and Infrastructure (Adapters). Hence, some refer to this architecture as ports and adapters architecture.Note the direction of dependency in this Image; the outside can refer to the inside but not vice versa; this is key to protecting the inside.If the domain logic starts to refer outside, for example, let’s say the domain logic has framework library references or controller references, the moment we change the framework, the blast radius is enormous.It is time to dive deep into the hexagonal architecture and learn how it works.The application we develop has a driven side (Users, Tests, SPA, Mobile Apps, Queues, Topics) and a driven side (repository, Remote Application, Queue)Driver side: The communication is started by the driverDriven side: The communication is started by the applicationPort: A Port is an interface that defines a set of operations that one side of the application (typically the core business logic) exposes to the other side. Think of a port as a contract representing a specific use-case’s input and output methods.Driver Port: Driver Ports are the primary means by which the external actors (like users or external systems) drive the application to perform some action. It’s the interface that the application’s primary use cases implement.Driven Port: Driven Ports represent the application’s secondary interfaces to communicate with external systems or components (like databases, messaging systems, or third-party services).Adapters: Adapters bridge the application and the external actors or systems. They match the technology-specific methods and data formats to the use-case-specific Ports.Driver Adapters: A Driver Adapter interprets and translates specific technology requests into technology-agnostic requests that are fed into the application through a Driver Port.Driven Adapters: A Driven Adapter takes the application’s technology-agnostic requests, as determined by the Driven Port, and converts them into specific technology interactions or requests.Let’s take real-world scenario-based examples.Scenario 1 : Transitioning from REST to GraphQLBusiness wants to provide efficient data retrieval for mobile clients, hence required to redefine data retrieval methods more flexibly, adhering to the new GraphQL standards without altering the business logic contracts defined in the ports.Current State: Web REST API AdapterOperations: Handle HTTP methods such as GET, POST, PUT, and DELETE.Data Exchange: Exchanges data in JSON or XML format.New State: Web GraphQL AdapterOperations: Handles queries and mutations, offering more flexibility and efficiency in fetching.Data Exchange: Allows clients to request the data they need, avoiding over-fetching or under-fetching of data.We can swap the REST adapter with the GraphQL adapter without changing the core business logic.Scenario 2 : Switching from Kafka to RabbitMQThe organisation got a better deal with RabbitMQ through a strategic partnership, and running the current Kafaka cluster is expensive.Current State: Kafka Consumer AdapterMessage Processing: Processes stream of messages using topics and partitions.New State: RabbitMQ Consumer AdapterMessage Processing: Utilises queues for message processing and supports various messaging protocols.Allows an easy swap of messaging systems without affecting the core application.Scenario 3 : Changing from SQL to NoSQLThe business is scaling at neck-breaking speed; the relational database does not support horizontal scaling. The company wants you to switch to a NoSQL database to scale horizontally, meeting the demands of high-velocity data.Current State: SQL Database AdapterData Storage: Relational data storage using tables, rows, and columns.New State: NoSQL Database AdapterData Storage: Flexible data storage, supports various data models including document, key-value, wide-column, and graph.Allows for the easy implementation of different data models without affecting the business logic.The next step is to combine these two approaches. Domain-Driven-Desing to model the business process and hexagonal architecture to protect that business logic from the outside. That is as simple as that.The domain logic and model comprise all the Domain-Driven-Desing constructs we learn in the domain-driven design segment.The rules set by Hexagonal Architecture govern the application and infrastructure layer.Let’s explore this with an example application.The business requirements for this sample application are simple, and the logic it implements is also simple. Hence, the focus should be on the architecture and how things glue together instead of the business logic.At the moment the business requirement is as follows.This contains only the use cases that act as a domain port.Example application can be found at https://github.com/KalpaD/ddd_with_hex/tree/mainIn the ever-evolving landscape of software development, Domain-Driven Design and Hexagonal Architecture harmonising can be used to build maintainable applications effectively. This strategy is exemplified in a given project; even though it is written in Java, the concepts and design approach are language and framework-agnostic. Let this be a call to action for product engineering teams to embrace these battle-tested methodologies, embarking on a journey towards building resilient, maintainable software applications.Netflix Technology Blog : https://netflixtechblog.com/ready-for-changes-with-hexagonal-architecture-b315ec967749What is DDD — Eric Evans — DDD Europe 2019 : https://youtu.be/pMuiVlnGqjk?feature=sharedEvent Storming — Alberto Brandolini — DDD Europe 2019 : https://youtu.be/mLXQIYEwK24?feature=shared----Level Up CodingSenior Software Engineer | Cloud | API | System DesignKalpa Senanayake--6Prathamesh GadekarinLevel Up Coding--5Ahmed BesbesinLevel Up Coding--18Kalpa Senanayake--7Alex Dorand--Alessandro Traversi--Anand Mohan--Marta Vila GarridoinBetter Programming--2Pithom Labs--1Rajeshvelmani--HelpStatusWritersBlogCareersPrivacyTermsAboutText to speechTeams



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Domain-Driven Design Meets Hexagonal Architecture: A Guide to Future-Proof Applications

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×