猿问

创建聚合根及其子对象(实体)的正确流程是什么?

我正在将我的数据驱动设计重构为领域驱动,我对代码的结构有几个问题


我有一个User entity


namespace Planner\Domain\Entities;


class UserEntity {

    private $userId;


    private $weight;

    private $height;


    public function __construct(int $id, float $weight, float $height){

        $this->userId = $id;

        ...

    }

}

我还有一个Settings entity应该与用户对象创建一起创建的。


namespace Planner\Domain\Entities;


class SettingsEntity {

    private $id;


    private $userId;


    private $darkTheme;


    public function __construct(int $id, int $userId, bool $darkTheme){

        $this->id = $id;

        $this->userId = $userId;

        $this->darkTheme = $darkTheme;

    }

}

Settings 对象不能没有 User 存在,因此 settingsEntity 应该由 User 实体管理。这意味着,用户实体不仅仅是一个实体,它应该是一个聚合根。


目前,当用户点击“创建账户”时,App 向 发出 API 请求example.com/user/save,控制器动作将请求对象发送到Planner\Application\Services\UserServicesave 方法。


namespace Planner\Application\Services;


use Planner\Domain\Entities\UserEntity;

use Planner\Domain\Entities\SettingsEntity;

use Planner\Domain\Repositories\UserRepository;

use Planner\Application\Services\SettingsService;


class UserService {


    public function __construct(UserRepository $repo, SettingsService $settings){

        $this->userRepository = $repo;

        $this->settingsService = $settings;


    }


    public function save($request){

        $user = new UserEntity(...);


        $settings = new SettingsEntity(...);


        if(!$this->userRepository->save($user) || !$this->settingsRepository->save($settings)){

            throw new UserCreationFailedException();

        }

    }

}

问题是,我不知道是否应该一次性创建用户实体和设置实体(从理论上讲,因为我没有用户聚合根,只有一个用户实体)用户聚合根或者当前方式有效。


我还有其他需要同时创建的实体...与设置对象相同,我只是将它们添加到ifUserService 保存方法中的检查中(上面的代码)还是应该全部在用户聚合下,例如:


namespace Planner\Domain;


class User extends AggragateRoot {


    public function __construct(UserRepository $repository){

        $this->repository = $repository;

    }


    public function createAccount($request){

        $user = new UserEntity(...$request);

        $settings = new SettingsEntity(...$user);


        $this->repository->save($user, $settings);

    }

}


哔哔one
浏览 177回答 2
2回答

MYYA

所有顶级实体都是聚合根。在您当前的设计中,UserEntity和SettingsEntity是有效的聚合根 (AR)。AR 是事务性和一致性边界。AR 的作用是确保与它们封装的数据相关的不变量永远不会被破坏,即使通过并发也是如此。AR 应该设计得尽可能小,因为它们可以防止对它们保护的数据进行并发修改。为了从 AR 模式中受益,您必须努力将 AR 视为事务边界,因此对于大多数用例(可能存在例外),尝试只修改每个事务的单个 AR。但是,该规则在创建 AR 时并不适用,因为在创建时并发冲突不应该是常见的。这里有两种显而易见的潜在设计,正确的一种取决于实际的业务不变量和您想要做出的妥协。User并且Settings具有交叉不变量,这意味着不变量User可能取决于状态,Settings反之亦然。在这种情况下User,并且Settings必须是同一一致性边界的一部分。您很可能拥有UserAR 和Settings生活在User.User并且Settings可以独立进化(除了他们的创造)。在这种情况下,您很可能希望将User和Settings作为自己独立的 AR,但在同一个事务中创建两者(或不创建 - 最终一致性)。请注意,在 AR 上使用工厂方法来创建另一个工厂方法通常很优雅。transaction {      user = new User(…)      settings = user.initSettings(...)      userRepository.save(user);      settingsRepository.save(settings);}从那时起,User将Settings在不同的事务中进行修改。PS:我建议删除诸如“实体”之类的技术前缀。语言是 DDD 的关键,我怀疑领域专家在他们的语言中使用“UserEntity”(甚至可能不是“User”)或“SettingsEntity”这个词。

温温酱

这取决于它是否是新用户。如果它是现有用户,则处理它的一种方法是使用存储库“水合”用户和存储中的设置。然后修改。如果是新用户,您可以使用工厂实例化聚合根(用户实体)并使用符合 UL 的工厂方法从输入生成设置。拥有用户对象后,将其发送到存储库以进行持久化。
随时随地看视频慕课网APP
我要回答