سؤال

I was discussing a project structure of mine with a friend and he pointed out an interesting point about how my architecture seems to introduce complexity and over abstraction into my layers by having "clones" of existing models.

My project structure, overall, is such:

[UIL] ---- Consumption / U.I. Layer (Just for the sake of completeness with respect to the architecture’s explanation)
    - Models (ViewModels & Mapping)
    - Controllers
    - Views

[API] ---- Service/API Layer (We call it the API layer because we use actual services from others that would be confusing if we called our stuff services & clash...so we call these classes with the suffix of Api (i.e. UserApi, ProductApi)) (coordinates the lower level calls & interactions into consumable higher level facades)
    - Api Models (Initial properties are the same to that of DTO models) (Rich Domain Models that utilize dependency injection to BLL api's, these are passed to Consumption / UI Layer if needed)
    - Interfaces(self-explanatory)
    - Adapters
    - Api's / Services
    - Mixins

[BLL] ---- Business Logic Layer (Domain)
    - Models (holds specific business logic rules & properties & engine references for corresponding api models that should not be publicly exposed)
    - Interfaces
    - Engines

[DAL] ---- Data Access Logic Layer
    - Models (Database matching anemic DTO's)
    - Interfaces (...)
    - Connections
    - Factories
    - Repositories
    - UnitOfWork's
    - Contexts

He notes that I have multiple "clones" of the original models from my DB / DAL by the fact that each of the DAL, BLL, and API models hold the same properties that are in each other (usually not all, but they all link together via some). I think however that they progress nicely from anemic (just for Database interactions) to rich complex "domain" models with fully flushed out methods, fully expanded properties, linked models, linked model api's (behind methods / DI), validation, business logic api's (behind methods / DI), etc. These can be safely passed to any consumption layer for further usage or for mapping to view models.

  1. I do not see a reason to clog my anemic DAL models with the logic in these models because nothing should ever know about my DAL other than the API/Service layer. Correct? This also breaks separation of concerns since now my DAL DTO model now maps to the database, business logic, view, etc. and can be sent back to the database...or somehow, I am missing something?

  2. If I do use my DAL models for everything, my consumption / UI layer would then reference DAL.Models.Product for example and now my DAL is exposed. This is the antithesis of separating & decoupling the layers, right? If my understanding is correct, nothing above the API/Service layer should know about anything below it (i.e. DAL, BLL, and/or other layers below), correct?

  3. The Api's Models are more fully customized to what a consumption layer would want to see versus a database (i.e. no foreign id's, "entity" classes, had clearly modeled behavior via methods, DI API's, etc.), this makes the front-end decoupled from the back-end if it changes, correct? Also, they interface nicely with ViewModels by just needing to use them as the ViewModel or via quick mapping to a view model.

I am not saying that I am right or that my friend is right, but he did bring up an interesting point that I wanted to explore. So far research is somewhat sporadic with people in many different camps.

هل كانت مفيدة؟

المحلول

It sounds like your friend is suggesting a more ADM (Anaemic Domain Model) approach.

Essentially you just have your Method-less DTO model and use it through all the layers. Your Data layer is not exposed because the model exists in its own project/dll/class lib/namespace

Instead of methods on the Model, your methods are in Services. So instead of Order.Purchase() you have SalesService.Purchase(order)

You don't have to completely abandon the OOP approach though, you could simply use your Business Models, with methods included, throughout. After all these are presumably mapped to the DTOs, Api Models, etc in your current setup anyway.

The problem of 'clones' becomes particularly obvious when you have a service orientated architecture. I've seen systems with OrderPlatformCustomer, InventoryCustomer, ReportingCustomer etc. all ultimately read out of the same db, but with slightly different fields, property names and different Methods.

It makes it difficult to talk about "A Customer" because it no longer has a single meaning and set of properties. While with an ADM approach you reuse the same data struct across everything. "A Customer" always has the same properties and relationships to other objects. It can just be processed in different ways.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى softwareengineering.stackexchange
scroll top