📜 ⬆️ ⬇️

Introduction to the Rich Domain Model

Recently, you can hear a lot of abbreviations that end in DD: TDD, BDD, FDD, etc. I was interested in one of the representatives of the "DD-family" - DDD, Domain Driven Development. I will not describe here all the details of this methodology, because all the necessary information can be easily found on the network. My goal is to tell about the most important concept of DDD, about the Rich Domain Model and to show the main nuances of implementation with a small example.

Rich Domain Model contrasted with Anemic Domain Model. “Thick” model is characterized by state and behavior, in contrast to “thin”, where there is only a state. On the topic I can recommend a presentation . In the end, he decided for himself: there is nothing wrong with anemic, but this is a procedural approach with all the ensuing consequences. I don’t want to go deeper into the wilds yet, just assume that the Rich Domain Model is a model with state and behavior (business logic).
On closer inspection, I encountered difficulties in understanding the Repository pattern and its difference from DAO, but more on that later.
The basics of using the Rich Domain Model will take a look at the example of a mini blog service (it seems to me that a blog recently claims the role of Hello, World! With reference to the web).

Let's start, as expected, with the subject area:

The essence of "Post":
public class Post {
private String text;
private Date date;
private List <Comment> comments;

public Post( String text) {
this .text = text;
this .date = new Date();
this .comments = new ArrayList <Comment>();
}

// getters and setters...
}

* This source code was highlighted with Source Code Highlighter .
public class Post {
private String text;
private Date date;
private List <Comment> comments;

public Post( String text) {
this .text = text;
this .date = new Date();
this .comments = new ArrayList <Comment>();
}

// getters and setters...
}

* This source code was highlighted with Source Code Highlighter .
public class Post {
private String text;
private Date date;
private List <Comment> comments;

public Post( String text) {
this .text = text;
this .date = new Date();
this .comments = new ArrayList <Comment>();
}

// getters and setters...
}

* This source code was highlighted with Source Code Highlighter .


')
Entity “Comment”:
public class Comment {
private String text;
private Date date;

public Comment( String text) {
this .text = text;
this .date = new Date();
}

// getters and setters...
}


* This source code was highlighted with Source Code Highlighter .
public class Comment {
private String text;
private Date date;

public Comment( String text) {
this .text = text;
this .date = new Date();
}

// getters and setters...
}


* This source code was highlighted with Source Code Highlighter .
public class Comment {
private String text;
private Date date;

public Comment( String text) {
this .text = text;
this .date = new Date();
}

// getters and setters...
}


* This source code was highlighted with Source Code Highlighter .



Users can post comments on posts, so add a comment method to the post model. On this method, we restrict ourselves to the entire business logic of our prototype:
public Comment comment( String text) {
Comment comment = new Comment(text);
this .comments.add(comment);
return comment;
}

* This source code was highlighted with Source Code Highlighter .
public Comment comment( String text) {
Comment comment = new Comment(text);
this .comments.add(comment);
return comment;
}

* This source code was highlighted with Source Code Highlighter .
public Comment comment( String text) {
Comment comment = new Comment(text);
this .comments.add(comment);
return comment;
}

* This source code was highlighted with Source Code Highlighter .



Our application must ensure the preservation of objects, so that the queue for the Repository. The behavior of the Repository is similar to the behavior of a regular java-collection. A quick look at this pattern gives the impression that there is no difference from the DAO, but it is not. Consider an example of a session with DAO:
Item item = new Item();
item. set ...
itemDao.insert(item);
...
Item item = itemDao. select (itemId);
item. set ..
itemDao.update(item);


* This source code was highlighted with Source Code Highlighter .
Item item = new Item();
item. set ...
itemDao.insert(item);
...
Item item = itemDao. select (itemId);
item. set ..
itemDao.update(item);


* This source code was highlighted with Source Code Highlighter .
Item item = new Item();
item. set ...
itemDao.insert(item);
...
Item item = itemDao. select (itemId);
item. set ..
itemDao.update(item);


* This source code was highlighted with Source Code Highlighter .


Here is an example of working with Repository:
Item = new Item();
item. set ...
itemRepository.add(item);
...
Item item = itemRepository. get (itemId);
item. set ...

* This source code was highlighted with Source Code Highlighter .
Item = new Item();
item. set ...
itemRepository.add(item);
...
Item item = itemRepository. get (itemId);
item. set ...

* This source code was highlighted with Source Code Highlighter .
Item = new Item();
item. set ...
itemRepository.add(item);
...
Item item = itemRepository. get (itemId);
item. set ...

* This source code was highlighted with Source Code Highlighter .


And that's it! There is no update. As you can see, working with repository is similar to working with a collection of objects, while working with a DAO we always have to manually record the state of an object in the repository.

Here is the actual interface:

public interface PostRepository {
void add(Post post);
void remove(PostId postId);
Post get (PostId postId);
List <Post> getAll();
}

* This source code was highlighted with Source Code Highlighter .
public interface PostRepository {
void add(Post post);
void remove(PostId postId);
Post get (PostId postId);
List <Post> getAll();
}

* This source code was highlighted with Source Code Highlighter .
public interface PostRepository {
void add(Post post);
void remove(PostId postId);
Post get (PostId postId);
List <Post> getAll();
}

* This source code was highlighted with Source Code Highlighter .



I need an identifier to access the element, so I created the PostId helper class:
public class PostId {}

public class Post {
private PostId postId;
private String text;
private Date date;
private List <Comment> comments;
...
}


* This source code was highlighted with Source Code Highlighter .
public class PostId {}

public class Post {
private PostId postId;
private String text;
private Date date;
private List <Comment> comments;
...
}


* This source code was highlighted with Source Code Highlighter .
public class PostId {}

public class Post {
private PostId postId;
private String text;
private Date date;
private List <Comment> comments;
...
}


* This source code was highlighted with Source Code Highlighter .



I purposely do not use numerical identifiers, which are usually used as primary keys in the database. We are going from the domain and do not need to know anything about the database. Subsequently, any repository can be implemented and not necessarily based on the database, where, for example, there may be no numeric identifiers.

But a simple implementation of PostRepository:

public class InMemoryPostRepository implements PostRepository {
private Map<PostId, Post> identityMap = new HashMap<PostId, Post>();

@Override
public Post get (PostId postId) {
return this .identityMap. get (postId);
}

@Override
public List <Post> getAll() {
return new ArrayList <Post>( this .identityMap.values());
}

@Override
public void add(Post post) {
PostId postId = new PostId();
post.setPostId(postId);
this .identityMap.put(postId, post);
}

@Override
public void remove(PostId postId) {
this .identityMap.remove(postId);
}
}


* This source code was highlighted with Source Code Highlighter .
public class InMemoryPostRepository implements PostRepository {
private Map<PostId, Post> identityMap = new HashMap<PostId, Post>();

@Override
public Post get (PostId postId) {
return this .identityMap. get (postId);
}

@Override
public List <Post> getAll() {
return new ArrayList <Post>( this .identityMap.values());
}

@Override
public void add(Post post) {
PostId postId = new PostId();
post.setPostId(postId);
this .identityMap.put(postId, post);
}

@Override
public void remove(PostId postId) {
this .identityMap.remove(postId);
}
}


* This source code was highlighted with Source Code Highlighter .
public class InMemoryPostRepository implements PostRepository {
private Map<PostId, Post> identityMap = new HashMap<PostId, Post>();

@Override
public Post get (PostId postId) {
return this .identityMap. get (postId);
}

@Override
public List <Post> getAll() {
return new ArrayList <Post>( this .identityMap.values());
}

@Override
public void add(Post post) {
PostId postId = new PostId();
post.setPostId(postId);
this .identityMap.put(postId, post);
}

@Override
public void remove(PostId postId) {
this .identityMap.remove(postId);
}
}


* This source code was highlighted with Source Code Highlighter .



Now create a service layer. I want to note that there should be no business logic in the service layer, it is only needed to designate the boundaries of the application, delegating user calls to domain objects that are retrieved from the repository:

public class BlogService {
private PostRepository posts;
public void setPostRepository(PostRepository posts) {
this .posts = posts;
}

public Post post( String text) {
Post post = new Post(text);
this .posts.add(post);
return post;
}

public Comment comment(PostId postId, String text) {
Post post = this .posts. get (postId);
return post.comment(text);
}

public Post get (PostId postId) {
return this .posts. get (postId);
}

public List <Post> getAll() {
return this .posts.getAll();
}

public void delete(PostId postId) {
this .posts.remove(postId);
}
}

* This source code was highlighted with Source Code Highlighter .
public class BlogService {
private PostRepository posts;
public void setPostRepository(PostRepository posts) {
this .posts = posts;
}

public Post post( String text) {
Post post = new Post(text);
this .posts.add(post);
return post;
}

public Comment comment(PostId postId, String text) {
Post post = this .posts. get (postId);
return post.comment(text);
}

public Post get (PostId postId) {
return this .posts. get (postId);
}

public List <Post> getAll() {
return this .posts.getAll();
}

public void delete(PostId postId) {
this .posts.remove(postId);
}
}

* This source code was highlighted with Source Code Highlighter .
public class BlogService {
private PostRepository posts;
public void setPostRepository(PostRepository posts) {
this .posts = posts;
}

public Post post( String text) {
Post post = new Post(text);
this .posts.add(post);
return post;
}

public Comment comment(PostId postId, String text) {
Post post = this .posts. get (postId);
return post.comment(text);
}

public Post get (PostId postId) {
return this .posts. get (postId);
}

public List <Post> getAll() {
return this .posts.getAll();
}

public void delete(PostId postId) {
this .posts.remove(postId);
}
}

* This source code was highlighted with Source Code Highlighter .



That's all. Of course, this example is difficult to see all the advantages of the Rich Domain Model, because the example is too trivial. But I hope that he will help someone in practical implementation. When I tried to figure it out on my own, the query in google “rich domain model example” did not give anything intelligible. But now, when there is already a more or less holistic picture in my head, I decided to share my findings with the community. If I like the article, I can write a sequel, in which there will be a more realistic implementation based on Hibernate and I will try to show in practice such an important feature as Persistence Ignorance.

Separately, I would like to say about the 2 discoveries that I made for myself looking for materials on the topic in the network:
1) If Hibernate or any other ORM is used, then the use of a DAO is inappropriate.
2) Given the great popularity of the Anemic Domain Model, you can make a bold statement that OOP is used quite rarely.

Here is the project itself.

Source: https://habr.com/ru/post/87812/


All Articles