System description
This is a simple eCommerce application containing a Products inventory managed by an Admin. The Products in inventory/repository will show in the User App. Admin has the ability to mark ON/OFF a Product. ON = display in User App, OFF = do not display. Products are maintained in a single table (Products) in database.
This system is built (read: attempted) on DDD principles.
The Products data in db is accessed in code via .NET Entity Framework.
There are two different Applications (Admin App and User App) created.
- Admin App can see ALL Products.
- Domain name =
Admin
. - Does the simple CRUD operations.
- Ignoring other setup here as it is pretty straightforward.
- Domain name =
- User App should only see "ON" Products.
- Domain name =
StoreView
. (FYI - Entities from Admin domain are not mixed/reused here.) - AggregateRoot = NONE. I realize that UserApp cannot modify a Product. So, decided to create Product as a DTO/POCO class.
- Repository =
IProductRepository
, hasGetProducts()
.
- Domain name =
My Problem
Where to place this (ON/OFF) "business logic"? (BTW, this is a business logic, right? OR is it an "application logic"?) According to DDD, business logic should be maintained in the Domain layer. However, this is a "query" (product.Status == "ON"). Where should this logic be put - Domain Entity, Domain Service, Repository or Application?
I found these two questions closest to my problem, but I can't find the answer, alas.
Handling Business Logic/Validation Race Conditions
How to completely avoid business logic in DAL?
Approach I - Query in Repository
Write the query business logic in IProductRepository, e.g.
public IEnumerable<Product> GetProducts()
{
return db.Products.Where(p => p.Status == "ON").ToList();
}
Disadvantage: This does not look like how to do things in DDD.
Approach II - Query in Application Layer
Get ALL Products from repository, and then apply condition in Application Service/Controller/somewhere like that.
Disadvantage: ALL Products will be loaded in memory and then filtered. Also, I am not sure if this conforms to DDD.
Approach III - Query in an Aggregate Root
Have an AggregateRoot, say, Store
. Store contains a property for Products. Get entire Store, including ALL Products from repository, and the "filtering" happens in Products' getter.
ICollection<Product> _products;
ICollection<Product> Products
{
get { return _products.Where(p => p.Status == "ON").ToList(); }
private set { _products = value; }
}
Disadvantage: ALL Products will be loaded in memory. Also, keeping ALL Products throughout Store entity's life-time seems really odd.
Approach IV - Maintain two separate Product tables for the two domains
Maintain two separate tables for Products, maybe in two different schemas: admin.Products and storeview.Products.
admin.Products
will be populated by Admin users.
Raise a DomainEvent when Status
is updated for a Product.
A Domain/Application EventHandler subscribed to the above event will create/update storeview.Products
table.
NO query logic required in StoreView
domain or "UserApp".
Disadvantage: Additional complexity and infrastructure overhead of maintaining two separate tables in db.
Question
What is the most-appropriate solution, in terms of DDD, strictly?