Search code examples
c#asp.net-core.net-coreconstructordomain-driven-design

What should be the parameters of the entity ctor when mapping from dto to entity?


Domain-driven-design aggregate root Demo entity:

public class Demo : FullCompanyEntity<int>, IAggregateRoot
{
    public string? FirstName { get; private set; }
    public string LastName { get; private set; }
    public string? FullName { get; private set; }
    public string? Phone { get; private set; }
    public string? Email { get; private set; }
    public int? LeadId { get; private set; }
    public int? SaleId { get; private set; }
    public DateTime StartedAt { get; private init; }
    public DateTime? EndedAt { get; private set; }
    public int? TotalSpentTime { get; private set; }
    public double Latitude { get; private set; } // float8 psg type
    public double Longitude { get; private set; }// float8 psg type
    public Address? Address { get; private set; }
    public bool IsLive { get; private set; }


    private Demo() { } // for ef

    public Demo(double latitude,
                double longitude,
                string lastName,
                in int companyId,
                in int importerId,
                in int organisationId,
                in int distributorId,
                Address? address = null,
                string? firstName = null,
                string? phone = null,
                string? email = null,
                int? leadId = null) : base(companyId, importerId, organisationId, distributorId)
    {

        Guard.Against.LatitudeFormat(latitude);
        Guard.Against.LongitudeFormat(longitude);

        Latitude = latitude;
        Longitude = longitude;
        Address = address;
        FirstName = firstName;
        LastName = lastName;
        FullName = firstName.FullName(lastName);
        Phone = phone;
        Email = email;
        LeadId = leadId;

        StartedAt = DateTime.UtcNow;
        IsLive = true;
    }
}

Request Dto:

public class RequestDemoDto
    {
        public string? FirstName { get; private set; }
        public string LastName { get; private set; }
        public string? Phone { get; private set; }
        public string? Email { get; private set; }
        public int? LeadId { get; private set; }
        public int? SaleId { get; private set; }
        public double Latitude { get; private set; } 
        public double Longitude { get; private set; }
        public AddressDto? Address { get; private set; }
    }
  • There are two classes I shared above, one is domain entity and the other is request dto, which will be used for the API to receive requests.

  • There are a few questions that come to my mind here. Before asking them, I would like to explain the purpose of entity ctor.

  • The demo entity must not be initialized without required properties (required props latitude, longitude, lastName, companyId, importerId, organizationId, distributorId)

  • Even if some properties are not required for the demo entity, the user (the user who requests the web API, via dto) can set them the first initialization etc. firstName, phone, email, leadId

  • However, I created such a ctor in order to provide the features I wanted in the ctor above, but here a similar problem occurs.

When mapping from dto to demo or simply passing parameters to ctor as below;

var demo = new Demo(demoDto.Latitude, demoDto.Longitude, demoDto.LastName, companyId, importerId, organisationId, distributorId, firstName: demoDto.FirstName, phone: demoDto.Phone, email: demoDto.Email, leadId: demoDto.LeadId);

Some problems occur in the data:

  • For example, if phone or firstname is an empty string, then they will not be recorded as null in the database.
  • Or if leadId is 0 by default, database leadid will be 0 instead of null. But I want it to be null. But still, as seen above, what the user sets

Since the runtime will not know what it does, I need to switch all dto properties to ctor as above while mapping the entity from dto.

What should I do in such a situation, what kind of ctor should I design or what should the entity map from dto be like?

Note: FirstName, Phone, Email, LeadId, SaleId, Address must be null if no value is set in the database.

Note: is keeping null in the database a bad method? You can also comment on this issue.

Thank you to anyone who can help or not.


Solution

  • You could create a constructor that includes only the required values

    public Demo(double latitude,
                double longitude,
                string lastName,
                in int companyId,
                in int importerId,
                in int organisationId,
                in int distributorId) : base(companyId, importerId, organisationId, distributorId)
    {
    
        Guard.Against.LatitudeFormat(latitude);
        Guard.Against.LongitudeFormat(longitude);
    
        Latitude = latitude;
        Longitude = longitude;
        LastName = lastName;
    
        StartedAt = DateTime.UtcNow;
        IsLive = true;
    }
    

    Change the non-required properties to allow public set

    public string? FirstName { get; set; }
    public string? Phone { get; set; }
    public string? Email { get; set; }
    public int? LeadId { get; set; }
    public Address? Address { get; set; }
    

    Then you can do something like the following

    var demo = new Demo(demoDto.Latitude, demoDto.Longitude, demoDto.LastName, companyId, importerId, organisationId, distributorId)
    {
        FirstName = !string.IsNuulOrEmpty(demoDto.FirstName) ? demoDto.FirstName : null,
        Phone = !string.IsNuulOrEmpty(demoDto.Phone) ? demoDto.Phone : null,
        Email =  !string.IsNuulOrEmpty(demoDto.Email) ? demoDto.Email : null,
        LeadId = demoDto.LeadID > 0 ? demoDto.LeadID : null
    };
    

    You probably also want to do the following:

    private string? _firstName;
    public string? FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            _firstName = value;
            if (!string.IsNullOrEmpty(_firstName))
            {
                FullName = _firstName.FullName(LastName);
            }
        }
    }
    

    You don't seem to have done anything with Address - looks like you'll need to adapt the dto to an Address type or construct it appropriately when setting it, but that should follow a similar pattern.

    There's no problem storing nulls in most databases.