2

I'm trying to group the components of my system by funtionality. This is the analysis class diagram of my system model.

enter image description here

A service class that involves a Post entity might necessarily interact sometimes with Section, User, or also (to a lesser degree) PostVote. This also applies with the repository classes of each entity of course.

As an example, here's my PostService#newPost method:

@AuthenticationRequired //auth interceptor
public int newPost(@NotBlank String title,
                   @Size(max=65535) String body,
                   @NotNull String sectionName){
    //currentUser is an authentication related DTO
    User user = userRepository.findById(currentUser.getUsername());
    Section section = sectionRepository.findByNaturalId(sectionName);

    if(section == null)
        throw new SectionDoesntExistException();

    Post post = new Post();
    post.setTitle(title);
    post.setContent(body == null || body.isBlank() ? "" : body);
    post.setAuthor(user);
    post.setSection(section);
    post.setType(TEXT);

    return postRepository.insert(post).getId();
}  

The SectionRepository class is an included dependency that is used only to check for the Section existance and to get a managed entity. This happens in a lot of other places in my code base.

I could try of course to move this retrieval logic in PostRepository (the insert method calls SectionRepository#findByNaturalId and fails if none found), but this wouldn't answer my question: should the entities and/or its repositories be included in the package that outlines the vertical slice in my system in the first place? Or should they be put in a common entity and repository bucket that sits underneath all vertical slices in my system architecture?
The reason I'm asking this is that many see these vertical slices as independent codebases with minimal dependencies with peer subsystems. Introducing the entities in these package may add inevitably some dependencies.

What's the common practice for this kind of architectural pattern?

3 Answers 3

2

You are separating data from function. This does not pair well with what you're trying to do. Your class diagram contains only data structures. This is something you'll always have to touch when developing something new, or even when changing features. So changes will not be localized this way.

Based on your other post I think you can't quite get away from the remnants of the layered approach. You'll have to be much bolder. Design objects with functionality and hide the data! That's what objects are for. Try to get away from technical thinking (like data structures, repositories and services) and try to think about business behavior and how those interact.

As an exercise try to re-design these classes in a way that:

  1. No object has a getter. This mean no internal data is returned. Ever.
  2. All methods are some derivation of-, or a part of-, a business-related function / requirement.

If you can do this, you'll have a much easier time thinking about functions and behavior.

3
  • Thank you a lot for your answers. I've often seen the adding of behaviour in persistent entities as discouraged in favour of the addition of a data access layer: this (together with a bit of reading of the popular Clean Architecture book) has taken me into choosing the layered approach. The other way looks more suitable for OOP (And for the use cases I've written, also), although I haven't had luck finding good and consistent resources in regard. Do you have any resource/example in regard that i could peek into?
    – cidra
    Mar 2 at 19:18
  • 1
    I'm not talking about adding behavior to "persistent entities", but having objects with behavior instead of "persistent entities". You're still thinking about having data and operating on data. As for resources, here is one article of mine looking at where Clean Architecture leads you. Here is another one looking at just the theory. Mar 2 at 21:24
  • 1
    Yes, It's really hard to take off this data-only, behaviour-only mindset after years of having to deal with DAOs, Repository, ORMs, etc. Anyway, thanks a lot for sharing your articles, they're all really interesting
    – cidra
    Mar 3 at 12:15
1

A couple of thoughts

  1. I believe features are usually built on top of a domain. They orchestrate actions performed on entities of that domain.

    With this in mind, this shared behaviour could be placed in the domain.

  2. Features may have some common steps. But this is kind of "accidental" code duplication, meaning they currently share this actions but might not in the future.

0

I going to chime in here because although I agree with the content of the other answers, I'm not so sure they actually answer your question. Namely, how the pattern you are describing might take shape in your system in a way that abstracts data in favor of behavior.

What we want here is to exercise inversion of control (to your domain) in combination with a factory pattern:

Section section = sectionRepository.findByNaturalId(sectionName);

// we are ceding control to your domain to create a new post
Post post = section.newPost(author, title, body, type); 

The signature of Section#newPost may take different forms depending on your preferences, but you get the idea.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.