coding-styledomain-driven-designclean-architecturebusiness-logic

Entity/Domain purety dilemma in the clean architecutre/Domain driven design


Im working on a eCommerce system in which I try to implement the clean architecture.

But currently Im stuck a little bit.

So I have a use case called: CreateItemUseCase in which I create a Item (alias product) for the shop.

In this use case I call a method (createItemEntity()) of a Entity called ItemEntity.

This method creates just a data object with data like:

So now I need another method in the ItemEntity which validates the userId.

To create a Item the user needs to have a userId so the method in the ItemEntity would be called:

validateUserId()

This method should check if the user has a userId in the database and if not the Item creation would be imposible.

Now my question:

How do I validate the userId?

Should I have the validateUserId() method take a array as a parameter, In which all the User Id´s are saved... something like this:

validateUserId(toBeValidated: Int, allUserIds: Array[Int])
{
    // loop through the allUserIds to see if toBeValidated is in there ...
}

Or should I query the data in the method (which Im pretty sure, would violate the dependencie rule) like this:

validateUserId(toBeValidated: Int)
{
    // get all user id´s through a query, and check if toBeValidated is in there ...
}

Or should I do it completly different?


Solution

  • In general, entities should only contain logic that is operating on information (data) that is within the entity's scope. Knowing how to query if a user with a certain user id exists or not is not in the scope of the item entity.

    I think your motivation to keep all the logic for validation together is reasonable but on the other hand you should not introduce infrastructure dependencies (like talking to the database or user repository) to the entity. Knowing how to query if a user with a certain user id exists or not is not in the scope of the item entity.

    Or should I query the data in the method (which Im pretty sure, would violate the dependencie rule) like this

    Exactly, that's why it's usually best trying to avoid that to keep entities free from such dependencies. Introducing such dependencies can easily get out of hand and also increase complexity for testing such entities. If you need to do that it should be a very thought decision that justifies that.

    Should I have the validateUserId() method take a array as a parameter, In which all the User Id´s are saved... something like this

    This is not such a bad idea in general, because you would not make the entity dependent on infrastructure and provide the entity with all the data it needs for decision making. But on the other hand now you can run into another problem: bad performance.

    Now you would retrieve all user ids everytime you create an item. If you would do the check for the user's existence somewhere else this can be optimized much better.

    I suggest to ask the user repository beforehand if the user exists prior to performance the entity creation including all the other potentially required validations inside item entity that make sense there. The user repository could have a query that optimizes for just checking for the existence of this user by id.

    In case these two operations (asking for the user's existence and creating the new item) only happen at one place of the application I'd be pragmatic and perform the user existence check directly in the use case. If this would occur from different places in your application you can extract that logic into a separate (domain) service (e.g. item service) which deals with the repetitive flow operations working with the user repository and item entity.

    What you are dealing here with is a trade-off decision between domain model purity, domain model completeness and performance considerations. In this great blog this is named the Domain-Driven Design Trilemma. I suggest going through the reasoning in the article, I'm pretty sure it will help you coming to a final decision.