I am striving to have the most maintainable architecture possible in my application, but I cannot decide on a proper domain model design.
Here is an example domain model for a user:
public class User // Referred to as the domain model
{
// A model whose properties map exactly to a database table schema
private UserDataModel Model { get; set; } // Posts data back to the database
public string FirstName
{
get
{
return Model.FirstName;
}
set
{
// Validation & Observer logic here
Model.FirstName = value;
}
}
public string LastName
{
get
{
return Model.LastName;
}
set
{
// Validation & Observer logic here
Model.LastName = value;
}
}
public string FullName
{
return $”{FirstName} {LastName}”;
}
public Company Company // Other domain model with a similar design
{
get
{
return Model.Company;
}
set
{
// Validation & Observer logic here
Model.Company = value;
}
}
// List of other domain models with similar designs
public IList<Order> Orders { get; set; }
// …
internal User(UserDataModel user)
{
Model = user;
}
// Domain specific methods here
//(e.g. IsAdmin, CalculateBill, SendPasswordResetEmail, CanPlaceOrder, etc.)
}
Essentially, my concerns relate to what are the best practices for having an effective, efficient, and maintainable domain design that allows for robust consumption, strong encapsulation, and high extensibility / maintainability or scalability. Specific questions are:
Properties
a) Should my domain model contain properties similar to the underlying data model or properties that are only relevant to the application’s consumption of the domain model (i.e. properties like FullName, boolean flags like Dirty (updates occurred), Prototype (i.e, brand new dummy model for creation by client/consumer))?
b) Should the underlying data model be exposed publicly for access or should it only be accessible via methods? Should only parts of the data model be returned or the entire object?
c) Should the domain model include linked objects (i.e like the Company object in the example) or just the relevant value to that linked object (i.e. the Name property of the Company object instead of the entire object)?
d) Should setters be similar to above or only via methods called on the domain model or private methods called in the property setter (i.e. set { SetFirstName(value); } )? (Note: The setters will implement validation engine calls & observer calls & service calls, so they will be relatively chunky)
Construction
a) Should I approach construction of a new domain model from the client/consumer via several set methods or purely mapping the properties at the client/consumer level or via a factory / builder pattern?
b) Should my domain model completely hide the underlying data structure it represents (i.e. appears as if it is a object that was never part of a database)?
c) Should validation be done at construction level (i.e. as properties are set) or left up to the client/consumer calling an “IsValid” method on the domain model when appropriate?
Validation
a) Should validation errors be stored in a collection in the domain model or in a separate errors model / singleton class?
b) Should validation be done at the domain model level or data model level?
Retrieval
a) For lists of objects (i.e. orders), should the list be private and access via a “GetOrder(string orderNo)” method? Wouldn’t this be a violation of the Single Responsibility principle (I.e. the method is a pseudo repository / factory) or no?
b) How should retrieving an entire list be done without affecting the containing domain model or the domain models in the list? Should a clone be used despite it being potentially memory intensive (a user could have thousands of orders with their own linked domain models)?
I am ideally trying to avoid developing an unmaintainable domain design that is a pain to consume or test. I want the domain model to be as fluent & robust as possible thus allowing it to be read like prose to the client/consumer and representing the actual domain not the underlying data structures (i.e. it should be able to act as if it standing on its own without a backend).