In this course, we will learn the concepts of microservice and spring framework with a focus on Domain-Driven Design.
Learning Objectives
- Domain-Driven Design
Intended Audience
- Beginner Java developers
- Java developers interested in learning how to Build and Deploy RESTful Web Services
- Java Developers who want to develop web applications using the Spring framework
- Java Developers who want to develop web applications with microservices
- Java Developers who wish to develop Spring Boot Microservices with Spring Cloud
Prerequisites
- Basic Java knowledge
Hello there. In the previous video, we talked about domain services. In this video, we will talk about aggregates and repositories. So, let's begin talking about aggregates. Entities and value objects collaborate to form complex relationships that meet invariants within the domain model when dealing with large interconnected associations of objects, it's often difficult to ensure consistency and concurrency when performing actions against domain objects. The image in the slide shows a large object graph. Trying to treat this collection of objects as one conceptual whole is difficult and could result in performance problems for an application. For example, we would not want to block a customer from updating an address on her account just because the status of an earlier orders being changed at the same time.
These two things are unrelated and need not share a consistency or concurrency boundary. Domain-driven design has the aggregate pattern to ensure consistency and define transactional concurrency boundaries for object graphs. Large models are split by invariants and grouped into aggregates of entities and value objects that are treated as a conceptual whole. As shown in this image, you can distill the model into aggregates. Relationships between aggregate routes should be implemented by keeping a reference to the idea of another aggregate route and not a reference to the object itself. This principle helps to keep a boundary between aggregates and avoids the need to load large object graphs that are not required. The aggregate groupings at first glance look like a reasonable way to split the object graph. However, defining aggregate groups based purely on related concepts only goes so far to improve consistency and concurrency challenges.
Take the customer aggregate. If an address is amended at the same time that some personal details of a customer are changed, we could introduce blocking issues. These two concepts, although related to a customer, do not require an invariant; therefore, we can split these into two separate aggregates and use the same logic for a credit card, then group them all under a customer module. By the way, I would like to briefly explain the invariants. Invariants are rules that enforce consistency in the domain model. Whenever there is a change to an entity or aggregate, the business rules still apply. An example of an invariant is a rule stating that a customer must always have a complete address. To ensure that you adhere to this invariant do not give the user the opportunity to independently edit lines of the address, enabling it to become invalid. Instead, perhaps treat the address as a value object to ensure that a change results in the invariants remaining valid.
And lastly, I want to talk about the aggregate route. An aggregate route shown in the slide acts as the entry point into the aggregate. No other entity or value object outside of the aggregate can hold a reference to an object within the aggregate. Objects outside the aggregate can only reference the aggregate route of another aggregate. Any changes to objects in the aggregate need to come through the route. The route encapsulates the aggregate data and only exposes behaviors to change it. Okay now let's talk about the repository. A domain model needs a method for persisting and hydrating and aggregate. Because an aggregate is treated as an atomic unit, you should not be able to persist changes to an aggregate without also persisting the entire aggregate. A repository, as shown in the slide, is a pattern that abstracts the underlying persistence store from the model, allowing you to create a model without thinking about infrastructure concerns.
The repository is the mechanism that you should use to retrieve and persist aggregates. For view rendering, a repository is not required and querying against a data store is the most efficient method for reporting needs. A repository is an infrastructure concern. So, it is not always necessary to abstract away the underlying framework doing all the hard work. It can be more worthwhile to lean on object relational mapper or ORM, which is designed to remove the need to manually map objects to rows and properties to columns and write raw SQL frameworks to act as a repository. Examples include NHibernate, Raven database, and Entity Framework. Many developers get hung up on this pattern. Think of it simply as a method of persistence and rehydration. Treat it like infrastructure and don't get hung up on abstracting it away. Keep these tips in mind when implementing the repository. Think of it as an in-memory collection, performing add, remove, and retrieve operations on repositories.
Implement a known common access interface. Global interface for developers to know how to interact with a repository. The interface may contain not only lowest level methods but can contain higher level methods too if they are used by all classes that are using that interface. Methods for adding and removing. These are the actual implementations of the common interface and of course, they may differ depending on the repository type. Methods that predefine criteria for object selection predefine the most repeated queries in the form of methods. Now, let's look at the benefits of the repository. A repository provides a common abstraction for persistence. A repository promotes the separation of concerns. Domain logic and the user interface stay independent from data and backend data sources. Communicates design decisions. Only certain objects can access the repository.
That way, the repository itself can control access to the real storage. Enables testability. Repositories can be mocked for testing purposes. Improved maintainability. Tuning performance and adding caching operations can be easily implemented at the repository level. All right, so that's all I have to say about aggregates and repositories. Let's take a short break here and we'll talk about factory and module in the next video. See you in the next video.
OAK Academy is made up of tech experts who have been in the sector for years and years and are deeply rooted in the tech world. They specialize in critical areas like cybersecurity, coding, IT, game development, app monetization, and mobile development.