0
votes

I've been working on Website Development and recently started creating an eCommerce project with MVC.

I decided to develop it with onion architecture. As I understand my logic divided in 2 different areas: Application logic and Business Logic.

As you know I have 2 places to implement these logic: Domain layer for Business logic and Service Layer for Application logic. And also according to Onion Architecture, Domain Layer Has no relation to upper layer's such as Infrastructure Layer.

So I have to check these business logic in Domain Layer and throw appropriate exception's based on situations.For example "Having Positive Product Price" is a Business Logic. This logic doesn't need to have relation with Database. But what if I want to check "duplicate Name for product". I am sure that I should not check this logic in service layer. Where and how should i Check such these logic.

And if i have to have a Validation Layer, how to make this layer's relations? Thanks For your reply.

2
Seeing the domain driven design tag, are you using DDD's tactical patterns (Aggregate, Entity, Value Object, etc.) ?guillaume31
yes, I am using Domain Driven Design Architecture.Afsaneh

2 Answers

3
votes

I usually define repository interfaces in the domain layer, for instance ICustomer, IOrder,... and concrete objects CustomerRepository, OrderRepository,... in own Infrastructure layer. If a method in the domain layer needs something from the database during the normal execution, then I inject appropriate repository interface into the method from the service layer and just use database method that I need.

Sometimes it is better to define its own interface for a functionality, for example ICanCheckForDuplicateNames with a method CheckForDuplicates(string name) (this interface is in the domain layer). In the service layer I would instantiate an object which implements the ICanCheckForDuplicateNames (this object is defined in the infrastructure layer) and pass it as the parameter into the domain object.

And if you don't need the result of the database method but just need to invoke it, for example Log(string message), then you can define an event in the domain model and raise it during the execution, and the service layer would handle it. This way you don't need to pass anything to the domain layer class method from the service layer.

3
votes

One of the core concepts in DDD is that of an aggregate - essentially an entity or group of entities which can enforce a set of [invariants] in a transactionally consistent manner.

An invariant can be defined as a business rule that must be true at all times, other than during special times, such as while a business transaction is in process.

However, a more specific definition is to look at the invariants of an aggregate - these are the business rules that are enforced at all times - within a single aggregate or operation. This means that the invariant must be enforcable based on the data contained within the aggregate or passed into the aggregate on a command or method arguments.

Once way of describing this is that the invariant is enforced within the consistency boundary of the aggregate. It is called the consistency boundary because by following DDD recommendations and only modifying one aggregate per database commit, data consistency is only guaranteed within that boundary.

See http://www.informit.com/articles/article.aspx?p=2020371&seqNum=1 for an excellent article on aggregates and invariants.

Enforcing a unique constraint across the entire system is a common requirement - but if you think about it, it can't possibly be the responsibility of a single aggregate to enforce a unique name constraint because it requires knowledge of all the other instances of the aggregate to check - thus violating the principal of the consistency boundary.

There are a few approaches to tackling such problems:

  • Accept that the rule may not always be enforced, but that it will always 'eventually' be enforced (a concept known as eventual consistency).

    • You can have an event raised by the setting of the name which then triggers the execution of logic to check for duplicates and flag or alert so that the UI can draw the user's attention to items that need attention to correct the duplicate name.
  • Take a pragmatic approach and identify the most efficient place to enforce such a rule, outside the scope of the aggregate

    • e.g. the traditional and highly successful place for enforcing a unique constraint is in the database itself - most RDBMSs permit nominating a unique constraint on a field. Since the unique constraint will be checked within the scope of the database transaction during which the aggregate name is checked, this does give you atomic consistency on this business rule.
    • this option is constrained to cases where all your data is in one database - if you have sharded out, you may need to adopt the first option

To be honest, the second option is what I always do for 'universally unique' rules - if they truly are required - and the dataset size is such that I expect a single database. The first step though is to question the assumption that they are required, and examine the user stories associated with the aggregate to understand the real user requirements and what level of eventual consistency they will tolerate.