Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Where should complex business logic be in clean architecture? #33

Open
matteotrapani opened this issue Mar 28, 2024 · 3 comments

Comments

@matteotrapani
Copy link

Hi @amantinband !

First of all a huge thanks and bravo for the great work both here and on youtube.

The question in the title is pretty generic, but I wasn't sure what to write in so I'll try to give more details here.

I'll face soon a very complex project with a lot of functional requirement. As you know these bring a lot of business logic in the code and I'd love to use the clean architecture (along with CQRS) in the best and most powerful way.

In your example the CreateSubscriptionCommandHandler is in charge of checking whether an element already exists and create it.
In my case the CreateArticleCommandHandler should make the same verification with much more complexity (it's not just a matter of a field). If I try to figure out how to do it, I think to a Service working as "query/commands orchestrator" that contains all the BL, while the CreateArticleCommand is only in charge of writing the command to the DB.

What is your idea regarding this? How should I structure my project using CleanArchitecture?

Thanks!!

@Thijs153
Copy link

Thijs153 commented Apr 4, 2024

Hey @matteotrapani, (if you don't mind me answering ;) )

Could you give some information/examples on the BL you're talking about?

From a DDD's point-of-view you'd want to put most of the BL in the domain layer, i.e. a Rich Domain Model.
Checking whether an element already exists (in the database) is done in the application layer since the db is an external service - Infrastructure.

So deciding where you want to put your Business Logic depends on the Business Logic itself.

Also, since you're talking about a 'service', it might be worth looking into the difference between Application Services, and Domain Services.

@matteotrapani
Copy link
Author

Thank you for your return! Very appreciated!

I'll try to be as clear as possible regarding the application I'm working on, but it's quite hard to give all the functional details!

Figure out an online store like Amazon that sells many different kind of articles. Each article is defined by its set of properties and its category. The article is directly linked with the warehouse physical item, so it can't exist twice.

As you can imagine, the concept of "already existing" is not as simple as it seems because it actually means (sorry for the repetition ;) ) to look for an article, with the same set of properties and the same category.

At the same time the properties of the new articles must be "validated" and so on and so forth.

Before I try to propose an example of architecture, is the context clearer? Do you have any ideas?

Thanks!

@Thijs153
Copy link

Thijs153 commented Apr 5, 2024

Okay, so If I understand it correctly, you have an article that is defined by its properties + category, and before adding a new article to the database (in the CreateArticleCommandHandler) you need to check the following two things:

  1. Check whether the new article is valid (according to your business rules), and
  2. Check whether that new article doesn't already exist (I'm assuming in your database?).

As I mentioned earlier, you'd want to put as much of the Aggregate/Entity validation inside the entity itself. So validating whether the new entity is valid should be placed inside the Article class in your Domain Layer. You could have a static method like this (Using ErrorOr like Amichai, but you could also throw an exception or any Result object):

public class Article 
{
    // properties

    public static ErrorOr<Article> Create(... parameters)
    {
        // validate whether the parameters are valid, and return a new instance of an Article
    }
}

Then you need to check whether an article with these properties already exists, this can be done in the CommandHandler in the Application layer, or you could write a function for this check in your Repository for example, so you can reuse it in different handlers.

So your handler could look something like this:

public async Task<ErrorOr<Article>> Handle(CreateArticleCommand command, CancellationToken cancellationToken)
    {
        // 1. Try to create a new Article, validation is done in the Article itself.
        var result = Article.Create(... properties);

        if (result .IsError)
        {
            return result.Errors;
        }
 
       // 2. Check whether the article already exists.
        if (await _articleRepository.ExistsAsync(result.Value, cancellationToken))
        {
            return Error.Failure(description: "Article already exists.")
        }

       // 3. Add article to the database, and save changes.
        _repository.Add(result.Value);
        await _unitOfWork.SaveChangesAsync();

        return result.Value;
    }

Please let me know if this is something you can use, and just want to mention that I'm not an expert on this. This is just how I would do it. 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants