代码之家  ›  专栏  ›  技术社区  ›  Stark

在单个事务中实例化相同实体的正确方式?

  •  0
  • Stark  · 技术社区  · 3 年前

    我已经无计可施了,无法正确地为我的域建模( referring problem space ).

    这是一个由28天组成的膳食计划。用户注册后,他们会被重定向到入职以回答一些问题(保存为MealPlanResources VO),然后他们可以(从我们的内部食谱数据库中)选择28天中的每一天的膳食(如上图所示)。

    这些是一些用户用例,它们应该与Day实体进行交互。

    1. 用户可以添加/删除X天的膳食。
    2. 用户可以重置一天(取消所有膳食)。
    3. 用户可以将一天标记为完成/未完成(以防用户 还没有做好所有的饭菜)。

    到目前为止,这就是我对域建模的方式( folder structure ):

    MealPlan(AR)

    namespace App\MealPlan\Domain\Model;
    
    use App\MealPlan\Domain\Model\Calendar\Calendar;
    use App\MealPlan\Domain\Model\Calendar\CalendarId;
    use App\MealPlan\Domain\Model\Calendar\Day\Days;
    use Shared\Domain\Users\UserId;
    
    final class MealPlan {
    
        public function __construct(
            private MealPlanId $id, 
            private UserId $userId,
            private MealPlanResources $resources
        ){}
    
        public static function create(
            MealPlanId $id, 
            UserId $userId,
            MealPlanResources $resources
        ): MealPlan {
            return new self($id, $userId, $resources);
        }
    
        protected function createCalendar(
            CalendarId $calendarId, 
            MealPlanId mealPlanId,
            Days $days
        ): Calendar {
            return new Calendar($calendarId, $mealPlanId, $days);
        }
        
    }
    

    日历(实体)

    namespace App\MealPlan\Domain\Model\Calendar;
    use App\MealPlan\Domain\Model\MealPlanId;
    use App\MealPlan\Domain\Model\Calendar\Day\{Day, Days, DayState};
    
    final class Calendar {
    
        public function __construct(
            private CalendarId $id,
            private MealPlanId $mealPlanId,
            private Days $days
        ){}
    
        public static function create(
            CalendarId $id, 
            MealPlanId $mealPlanId, 
            Days $days
        ): Calendar {
            return new self($id, $mealPlanId, $days);
        }
    
        public function days(): Days {
            return $this->days;
        }
    
        public function resetDay(Day $day): void {
            
        }
    
        public function getDay(int $value): Day {
            foreach($this->days->getIterator() as $day){
                if($day->getDay() === $value){
                    return $day;
                }
            }
        }
    }
    

    日期(实体)

    namespace App\MealPlan\Domain\Model\Calendar\Day;
    
    class Day {
        
        private MealCollection $meals;
        
        public function __construct(
            protected DayId $day,
            protected CalendarId $calendarId,
            protected DayState $state
        ){}
    
        public function changeDayState(DayState $state): void {
            // if day doesn't have 3 meals, state can't be set to DayState::COMPLETED
            // throw error
    
            $this->state = $state;
        }
    
        public function addMeal(Meal $meal): void {
            // if meal already exists
            // throw error
            
            return $this->meals->add($meal);
        }
        
        public function removeMeal(): void {
            // remove meal 
        }
        
        public function getState(): DayState {
            return $this->state;
        }
    }
    

    基于以上内容,我认为应该在创建聚合根的过程中创建并持久化28天。但我不确定谁应该为增加28天并坚持下去负责。它是createCalendar方法中的MealPlan,还是Calendar工厂方法本身?

    0 回复  |  直到 3 年前
        1
  •  1
  •   VoiceOfUnreason    3 年前

    它是createCalendar方法中的MealPlan,还是Calendar工厂方法本身?

    需要注意的是:当第一本DDD书出版时(Evans,2003),领域实体是第五章中描述的模式,但生命周期管理模式(聚合、工厂、存储库)在第章中描述 .

    这里的部分想法是,您希望避免混合域代码和管道。是的,我们的模型需要日历和日期等的内存表示,但 商业 并不特别关心机器世界中这种情况是如何发生的细节。

    换句话说,“将所有东西正确连接起来”与管理域模型中的信息如何变化是一个单独的问题。

    因此,它可能遵循“单一责任原则”,我们希望“将所有东西连接起来”的代码在具有该责任的单个模块中隔离。

    在其他条件相同的情况下,这可能表明我们的第一个猜测应该是将尽可能多的“连接所有东西”代码放入工厂方法中,并让域实体专注于域。

    当您从持久信息存储加载聚合时,考虑一下您希望代码的样子可能会有所帮助。创建MealPlan看起来很像从数据库中加载MealPlan,主要区别在于您从“其他地方”获取信息。