Practice Archives - Domain Driven Design https://domaindrivendesign.org/category/practice/ Developing Thu, 03 Jun 2021 15:01:08 +0000 en-US hourly 1 https://wordpress.org/?v=6.4.2 https://domaindrivendesign.org/wp-content/uploads/2021/06/cropped-pngkey.com-asus-logo-png-1183990-e1622721891616-32x32.png Practice Archives - Domain Driven Design https://domaindrivendesign.org/category/practice/ 32 32 Domain-driven design – in PHP https://domaindrivendesign.org/domain-driven-design-in-php/ https://domaindrivendesign.org/domain-driven-design-in-php/#respond Thu, 03 Jun 2021 15:01:07 +0000 https://domaindrivendesign.org/?p=81 At Domain-driven design, we strive to keep our domain model outright bulletproof. In some cases, it is necessary

The post Domain-driven design – in PHP appeared first on Domain Driven Design.

]]>
At Domain-driven design, we strive to keep our domain model outright bulletproof. In some cases, it is necessary to enforce certain business rules when bringing a new object to life. If the construct is too complex or simply cannot be implemented by the object itself, then you should move the construct of the object to a dedicated class: factory.

Let me give you a quick example. Let’s say we have a meaningful TimeSpan object. Our initial TimeSpan implementation might look like this:

class TimeSpan
{
   /** @var \DateTimeImmutable **/
   private $from;   /** @var \DateTimeImmutable **/
   private $until;   public function __construct(\DateTimeImmutable $from, \DateTimeImmutable $until) 
   {
       if ( $from >= $until ) {
           throw new \InvalidArgumentException('Invalid time span.');
       }
       $this->from = $from;
       $this->until = $until;
   }   // Some other useful stuff goes in here...
}

Let’s say we don’t want the time interval to be unlimited, so we’ll set the maximum that can be set through the configuration. This is how it looks:

class TimeSpanConfiguration 
{
   /** @var \DateInterval **/
   private $maxTimeSpan;   public function __construct(\DateInterval $maxTimeSpan) 
   {
       $this->maxTimeSpan = $maxTimeSpan;
   }
}

So, we have a business rule that says: there can be no time span greater than the maximum that we have set. How can this be ensured? We can create instances of TimeSpan however we want, because nothing ever implements such a configuration. Fortunately, there are many solutions to this problem. I want to show you a factory:

timeSpanConfiguration = $timeSpanConfiguration;
   }   public function createTimeSpan(\DateTimeImmutable $from, \DateTimeImmutable $until): TimeSpan
   {
       // We just ask the configuration if the given from-until time span is valid.
       // That way we don't need any getters on the configuration. Neat.
       if ( !$this->timeSpanConfiguration->isValidTimeSpanFromUntil($form, $until) ) {
           throw new \DomainException('This time span is too long!');
       }       return new TimeSpan($from, $until);
   }}

Now, when creating a new TimeSpan instance, we simply use the TimeSpanFactory instead of the TimeSpan constructor. Thus, we always get a time interval that does not exceed the configured maximum.

Now you may be asking: So what ?, I can still create an invalid time interval when using the constructor. And yes, you’re right! And this can turn into a problem depending on the development team. If you have a fairly small development team, you can simply decide to always use TimeSpanFactory instead of the constructor directly. But there is a solution that forces you to use the factory pattern and that is Reflection. Using this approach can break something, but I’ll show you anyway;)

First, make the TimeSpan constructor private:

class TimeSpan
{
   /** @var \DateTimeImmutable **/
   private $from;   /** @var \DateTimeImmutable **/
   private $until;   private function __construct(\DateTimeImmutable $from, \DateTimeImmutable $until) 
   {
       if ( $from >= $until ) {
           throw new \InvalidArgumentException('Invalid time span.');
       }
       $this->from = $from;
       $this->until = $until;
   }   // Some other useful stuff goes in here...
}

Now you cannot create a TimeSpan instance using the new operator. PHP will throw a fatal error when you try to do this. Okay, but if we can’t create it that way, how then can our TimeSpanFactory create it? Reflection rushes to the rescue! Let’s take a look at our new TimeSpanFactory implementation:

timeSpanConfiguration = $timeSpanConfiguration;
   }   public function createTimeSpan(\DateTimeImmutable $from, \DateTimeImmutable $until): TimeSpan
   {
       // We just ask the configuration if the given from-until time span is valid.
       // That way we don't need any getters on the configuration. Neat.
       if ( !$this->timeSpanConfiguration->isValidTimeSpanFromUntil($form, $until) ) {
           throw new \DomainException('This time span is too long!');
       }       return $this->constructTimeSpan($from, $until);
   }   private function constructTimeSpan(\DateTimeImmutable $from, \DateTimeImmutable $until): TimeSpan
   {
       $class = new ReflectionClass(TimeSpan::class);
       $constructor = $class->getConstructor();
       $constructor->setAccessible(true);
       $timeSpan = $class->newInstanceWithoutConstructor();
       $constructor->invoke($timeSpan, $from, $until);       return $timeSpan;
   }}// Usage:
$factory = new TimeSpanFactory(new TimeSpanConfiguration(new \DateInterval('PT2D')));$timeSpan = $factory->constructTimeSpan(new \DateTimeImmutable('2019-02-17 17:00:00'), new \DateTimeImmutable('2019-02-17 18:00:00'));// Fails due too to long time span
$timeSpan = $factory->constructTimeSpan(new \DateTimeImmutable('2019-02-17 17:00:00'), new \DateTimeImmutable('2019-02-17 23:00:00'));// Also failes, but due to private constructor ;)
$timeSpan = new TimeSpan(new \DateTimeImmutable('2019-02-17 17:00:00'), new \DateTimeImmutable('2019-02-17 23:00:00'));

I agree, but I’d rather have a bulletproof domain model, and the likelihood that someone will create an invalid time interval and violate a business rule is not so great. It could be a junior developer who doesn’t know you should use a TimeSpanFactory, or even a senior who just forgot about Factory.

But as I said, it depends on your development team.

The post Domain-driven design – in PHP appeared first on Domain Driven Design.

]]>
https://domaindrivendesign.org/domain-driven-design-in-php/feed/ 0
Modeling entities https://domaindrivendesign.org/modeling-entities/ https://domaindrivendesign.org/modeling-entities/#comments Thu, 03 Jun 2021 13:37:19 +0000 https://domaindrivendesign.org/?p=54 By entity we mean an object of the subject area with a unique identifier. For example, let’s take

The post Modeling entities appeared first on Domain Driven Design.

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

The post Modeling entities appeared first on Domain Driven Design.

]]>
https://domaindrivendesign.org/modeling-entities/feed/ 1
Project structure https://domaindrivendesign.org/project-structure/ https://domaindrivendesign.org/project-structure/#respond Thu, 03 Jun 2021 13:07:45 +0000 https://domaindrivendesign.org/?p=43 Structuring the following .NET-projects as: Projects from the Libs folder are domain independent. They solve only their local

The post Project structure appeared first on Domain Driven Design.

]]>
Structuring the following .NET-projects as:

/App
  /ProjectName.Web.Public
  /ProjectName.Web.Admin
  /ProjectName.Web.SomeOtherStuff
/Domain
  /ProjectName.Domain.Core
  /ProjectName.Domain.BoundedContext1
  /ProjectName.Domain.BoundedContext1.Services
  /ProjectName.Domain.BoundedContext2
  /ProjectName.Domain.BoundedContext2.Command
  /ProjectName.Domain.BoundedContext2.Query
  /ProjectName.Domain.BoundedContext3
/Data
  /ProjectName.Data
/Libs
  /Problem1Resolver
  /Problem2Resolver

Projects from the Libs folder are domain independent. They solve only their local task, for example, generating reports, parsing csv, caching mechanisms, etc. Domain structure corresponds to BoundedContext’am. Domain projects are independent of Data. Data contains DbContext, migrations, configurations related to DAL. Data depends on Domain entities to build migrations. Projects in the App folder use an IOC container for dependency injection. Thus, it turns out to achieve maximum isolation of the domain code from the infrastructure.

The post Project structure appeared first on Domain Driven Design.

]]>
https://domaindrivendesign.org/project-structure/feed/ 0
Analytics first, then design, and only then – development https://domaindrivendesign.org/analytics-first-then-design-and-only-then-development/ https://domaindrivendesign.org/analytics-first-then-design-and-only-then-development/#respond Mon, 08 Mar 2021 12:48:54 +0000 https://domaindrivendesign.org/?p=39 You need to start not with a database structure or a set of classes, but with business processes.

The post Analytics first, then design, and only then – development appeared first on Domain Driven Design.

]]>
You need to start not with a database structure or a set of classes, but with business processes. We use BPMN and UML Activity in conjunction with test cases. Diagrams are easy to read, even by those unfamiliar with standards. Test cases in tabular form help to better identify borderline cases and resolve inconsistencies.

Abstract conversations are just a waste of time. People are convinced that the details are not significant and “there is no need to discuss them at all, because everything is already clear.” The request to complete the test case table clearly shows that the options are actually not 3, but 26.

Tables and charts are the main communication tool between business, analytics and development. In parallel with compiling BPMN diagrams and test case tables, we begin to write terms into the thesaurus of the project. The vocabulary will help later for the design of entities.

The post Analytics first, then design, and only then – development appeared first on Domain Driven Design.

]]>
https://domaindrivendesign.org/analytics-first-then-design-and-only-then-development/feed/ 0