0
votes

I've been trying to develop an N-Layered design with a WinForms application using Entity Framework.

Is the below sample flow correct for saving a new record in an n-layered design using Entity Framework?

  1. Presentation Layer

    A) UI performs light screen input data validations. Then UI converts view model to DTO and passes it to the Application Layer.

  2. Application Layer

    A) Application Layer sends DTO to Domain Model in Domain Layer

  3. Domain Layer

    A) Validate incoming DTO values for compliant business rules before creating new instance of Domain Model SoftwareRequest Entity.

    B) If all values are validated, then create new instance of Domain Model SoftwareRequest Entity.

    C) Exit to return control to Application Layer

  4. Application Layer

    A) Call Infrastructure Layer SoftwareRequest Repository and pass in the new Domain Model SoftwareRequest Entity which was provided by the Domain Model

  5. Infrastructure Layer Data Access

    A) SoftwareRequest Repository receives new Domain Model SoftwareRequest Entity from Application Layer.

    B) Add new Domain Model SoftwareRequest Entity to Entity Framework DBContext - context.SoftwareRequests.Add(NewDomainModelEntity)

    C) Save new entity - context.SaveChanges()

    D) Exit to return control back to Application Layer

  6. Application Layer

    A) Convert results of save operation to DTO

    B) Exit to return control back to UI with DTO containing results of adding new SoftwareRequest

  7. UI

    A) Convert received DTO to View Model

    B) Display View Model data to screen showing results of adding a new software request

--------- Below information added on 2/22/2016 at 6:49am PST ---------

Dependency Summary:

Presentation Layer - References Application Layer to make requests - References Domain Layer only for the purpose of working with interfaces describing DTO's which will be sent from the UI or received from the Application Layer

Application Layer - References Domain Layer interfaces describing DTO's. Also uses interface definitions for Domain Model Entities so it can convert entity responses from the Infrastructure Layer to DTO's which are returned to the UI. A reference to the Infrastructure Layer Data Access is also here so repositories can be access to perform CRUD operations after the involved Domain Model Entities have been validated for rules and values by the Domain Layer.

Domain Layer - Has no references to any layers above or below. Has no dependency injected services from any layer. This includes no tasks involving CRUD with infrastructure repositories. All requests such as for validation of rules receive DTOs containing all the necessary information to carry out requested domain tasks.

Infrastructure Layer Data Access - References Domain Layer interfaces describing Domain Model Entities which are used to perform Entity Framework operations in repositories (i.e. CRUD operations). Also references Domain Layer for definitions of interfaces for repositories which are implemented here in the Infrastructure Layer data access. DTOs are not used in this layer. This layer typically responds with Domain Model Entities to the Application Layer. The Application Layer converts all Domain Model Entity responses (i.e. IEnumerable) to DTOs which are sent back to the UI.

1
Can you post a description of the dependencies between your projects ? It looks as though your Domain has a reference to the Application layer - which is not recommended - or some classes are declared in the wrong layer.guillaume31
If it works and it produces the expected results it is "correct". There's really not much more we can say about this. We can argue about assembly dependencies (f.e. Domain -> Data Access, seemingly), but then we enter the opinion-based arena.Gert Arnold
Can't qualify as an answer but there is a decent course on Pluralsight where they explain DDD with examples using EF. You might be interested in it.Alexey Zimarev
Guillaume31, I've updated the description with a dependency summary. Gert Arnold, the above flow does work as expected. Alexey Zimarev, I'll also check the newest DDD video on Pluralsight released this year. I already saw the older one for enterprise apps. The above flow is not a pure DDD solution; I was not looking for one. It is an adaptation created to work with .NET, Entity Framework and WinForms. Thanks everyone for the input.Robertcode

1 Answers

1
votes

Based on your description, I suspect that the DTO types are declared in the Domain layer. It's not really where they belong because DTO's might change as the client changes and we don't want modify the Domain every time the client evolves. Also, DTO's can contain use case-level fields such as confirmPassword which are of no interest to the Domain.

Another thing is that the Application Layer is supposed to control the business transaction and not just be a facade to the Domain.

Here's how I would adjust it :

  1. Application Layer

    A) Application Layer maps DTO to new entity, either by calling its constructor or a Factory if construction is complicated

  2. Domain Layer

    A) In entity constructor or Factory, validate the values that the entity is initialized with (non-nullability, value ranges, dependent arguments etc.)

    B) Exit to return control to Application Layer

...

  1. Infrastructure Layer Data Access

    A) SoftwareRequest Repository receives new Domain Model SoftwareRequest Entity from Application Layer.

    B) Add new Domain Model SoftwareRequest Entity to Entity Framework DBContext - context.SoftwareRequests.Add(NewDomainModelEntity)

    C) Exit to return control back to Application Layer

  2. Application Layer

    A) Save new entity - context.SaveChanges()

    B) Convert results of save operation to DTO

Note that as you move business transaction control from the Infrastructure layer to the Application layer, you might have to introduce a new abstraction - typically Unit of Work - to abstract yourself from Entity Framework's DbContext.

Edit : a sample to illustrate this

/* Application layer */

public class SoftwareRequestDTO
{
    public string Name { get; set; }
    // your other SoftwareRequest data here
}

public class SoftwareRequestApplicationService
{
    // constructor etc...

    public void CreateSoftwareRequest(SoftwareRequestDTO dto)
    {
        using (var transaction = new BusinessTransaction()) // UoW or whatever - can also be constructor injected into the service
        {
            var softwareRequest = new SoftwareRequest(dto.Name);
            _softwareRequestRepository.Add(softwareRequest);
            transaction.Complete();
        }
    }
}

/* Domain layer */

public class SoftwareRequest
{
    public SoftwareRequest(string name)
    {
        // validation/guard clause
        if (name == null)
        {
            throw new DomainException("SoftwareRequest name cannot be null");
        }
        // your assignments here
    }
}