Home > Enterprise >  C# DDD without EF
C# DDD without EF

Time:11-20

I'm starting with DDD and I'm having one doubt. Suppose this classes (I'm using C# with Net Framework 4.7):

public class Address : ValueObject
{
    // Properties.
    public string Street { get; }
    public int Number { get; }


    public Address(string street, int number)
    {
        Street = street;
        Number = number;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Street;
        yield return Number;
    }
}

public class Customer : IAggregateRoot
{
    // Properties.
    public int Id { get; protected set; }
    public string Name { get; protected set; }
    public Address Address { get; protected set; }


    public Customer(string name, Address address)
    {
        if (string.IsNullOrEmpty(name)) throw new Exception(nameof(name));

        Name = name;
        Address = address ?? throw new Exception(nameof(address));
    }

    // For database purpose.
    protected Customer()
    {

    }
}

I have the interface ICustomerRepository and as infraestructure, I need to use raw SQL (SQL Server), I mean, no EF, NHibernate, etc. The approach I'm using within the repository implementation is to define a class "CustomerBuilder" who inherits from Customer, to be able to access protected fields. Is there any other way to achieve this? Because for each entity I need to load, I have to create a builder and repeat constructors and variable mapping.

Thanks!

CodePudding user response:

There isn't a single answer to your question. I can think of the following solutions:

  1. For each aggregate, create a "state DTO", a class with properties exposing the information you want to persist from your aggregate. Then create either a factory method or a constructor in your aggregate class that accepts this state DTO. Your repositories would new up the state DTO and pass it to the aggregate constructor/factory.

  2. Use AutoMapper, similar to point one, you'd need a DTO to initially load the information from the database. Then, instead of passing it to the aggregate to manually map to the aggregate's properties, use AutoMapper to automagically do the mapping for you.

  3. Build your own mapper using Reflection. You can create a series of helper methods to make it easy to map properties with private setters by name and type.

From the 3 solutions above, I think number 1 is the cleanest, but it doesn't save you much typing compared to what you are doing already. Option number 3 will be harder to set up, but once done, you should be able to create new repositories without much overhead. But effectively you are writing something that you can get for free by installing a nugget package.

CodePudding user response:

I've had the same problem and I've ended up using the Memento pattern. I've had to make some compromises, but iat the end the code works flawlessly and it's definitely simple to maintain.

I explain how is designed. In the domain layer I've and entity and a memento classes. The entity exposes just the domain functions, plus (here is the compromise) an extra function that allows to export itself to the memento. Whenever I need to use the content of the entity (normally inside the infrastructure layer) I call the function that returns the memento from my entity. Here, to simplify my work, I have another memento (that extends the original one and could be built using the original one or with the data from the infrastructure layer) that exposes a set of functions useful in that particular infrastructure context. So for example, for the persistence I've all the functions that build each type of map used by the SQL. The other function gets the SQL results and stores inside in the format used by the memento. Mementos are built in a way that's hard to build them outside the contexts where they're used.

I know that it's more code then what's normally someone writes with a ORM and also doesn't use any fancy functionality like reflection, but it's fast to test, it's working, it's doing what I expect and I don't have to play around with configuration files. It's just pre programming.

  • Related