By entity we mean an object of the subject area with a unique identifier. For example, let’s take a class describing a Russian company in the context of obtaining accreditation in a certain department.
[DisplayName("Company Name")]
public class Company
: LongIdBase
, IHasState<CompanyState>
{
public static class Specs
{
public static Spec<Supplier> ByInnAndKpp(string inn, string kpp)
=> new Spec<Supplier>(x => x.Inn == inn && x.Kpp == kpp);
public static Spec<Supplier> ByInn(string inn)
=> new Spec<Supplier>(x => x.Inn == inn);
}
// For EF
protected Company ()
{
}
public Company (string inn, string kpp)
{
DangerouslyChangeInnAndKpp(inn, kpp);
}
public void DangerouslyChangeInnAndKpp(string inn, string kpp)
{
Inn = inn.NullIfEmpty() ?? throw new ArgumentNullException(nameof(inn));
Kpp = kpp.NullIfEmpty() ?? throw new ArgumentNullException(nameof(kpp));
this.ValidateProperties();
}
[Display(Name = "ID")]
[Required]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Inn]
public string Inn { get; protected set; }
[Display(Name = "Name")]
[DisplayFormat(ConvertEmptyStringToNull = true)]
[Kpp]
public string Kpp { get; protected set; }
[Display(Name = "Status")]
public CompanyState State { get; protected set; }
[DisplayFormat(ConvertEmptyStringToNull = true)]
public string Comment { get; protected set; }
[Display(Name = "Date status")]
public DateTime? StateChangeDate { get; protected set; }
public void Accept()
{
StateChangeDate = DateTime.UtcNow;
State = AccreditationState.Accredited;
}
public void Decline(string comment)
{
StateChangeDate = DateTime.UtcNow;
State = AccreditationState.Declined;
Comment = comment.NullIfEmpty()
?? throw new ArgumentNullException(nameof(comment));
}
To select the right aggregates and relationships, one iteration is often not enough. First, I throw in the basic class structure, define one-to-one, one-to-many, and many-to-many relationships, and describe the data structure. Then I trace the business process structure against BMPN and test cases. If a case does not fit into the structure, then a mistake was made in the design and the structure must be changed. The resulting structure can be drawn up in the form of a diagram and additionally agreed with experts in the subject area.
Experts can point out design errors and inaccuracies. Sometimes in the process it turns out that there is no suitable term for some entities. Then I offer options and after a while a suitable one is found. The new term is added to the thesaurus. It is very important to discuss and agree on terminology together. This eliminates a large layer of misunderstanding problems in the future.
Do you think you can essentially inject dependencies through a constructor if they help you get more expressive when creating methods? For example, Product :: price () can refer internally to a strategy (PricingStrategy), which will determine the price of a product based on discount campaigns, manufacturer promotions, etc.?