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:
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.
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.