2

我正在尝试使用 DDD 对一个简单的域进行建模。数据库层使用实体框架实现,领域对象为 POCO。域具有具有 FirstName、LastName 和 Username 属性的用户实体。因此,域定义了为用户处理存储库的 IRepository。

现在域逻辑中的一个要求是不能存在具有相同用户名的两个用户。因此,当另一个具有相同用户名的用户已经存在时尝试添加新用户应该会引发异常。

IUnitOfWork unitOfWork = new UnitOfWork();
IRepository<User> users = unitOfWork.Users;

User user1 = new User() { Username = "jsmith", FirstName = "John", LastName = "Smith" };
users.Add(user1);   
users.Save();       // ok, new user added to the underlying database

User user2 = new User() { Username = "jsmith", FirstName = "Jim", LastName = "Smith" };
users.Add(user2); // exception here?
users.Save();     // or exception here?

这是应该进入添加新用户的 WPF 应用程序的代码示例。在这里,UnitOfWork 封装了 Entity Framework 的 DbContext 对象。

我的问题是我应该如何以及在哪里执行此域规则?尝试将用户添加到存储库时或者调用 Save() 方法时是否应该抛出异常?我是否应该创建域服务来添加新用户,然后在那里处理所有域逻辑规则?

另外,我应该抛出什么异常?我应该创建一些自定义域异常,例如 DuplicateUserException 或类似的东西吗?

4

2 回答 2

2

db 是该规则的最终执行者。现在,这是一个多用户应用程序吗?如果是这样,那么最务实的方式就是依靠db,捕获sql异常,然后抛出businessrule异常,该异常会被重定向到UI(即会触发错误信息)。

您可以验证域级别是否已存在名称(通过服务是最有效的方式),但这在并发环境中可能会失败。但是,对于单用户应用程序来说,它是一个优雅而干净的解决方案。我所说的单用户应用程序是指所有应用程序一次只为一个用户服务。如果您有 WPF 客户端和 Web 服务,那就是多用户应用程序。

您最好的选择是让持久性(在这种情况下是数据库)处理此规则,因为确保没有重复名称是存储库的责任(存储库不是一个愚蠢的存储桶,它是持久性的管理器)并且它解决了并发问题也是如此。

请注意,我没有提到 EF,因为与数据库通信的方式并不重要。如果明天您将切换到 Azure db,则解决方案仍然相同,只是实现的特定部分会有所不同。

于 2012-06-25T10:52:59.493 回答
1

我所拥有的最好的运气是使用自定义初始化程序并使用ExecuteSqlCommand如下方式在数据库上指定 UNIQUE CONSTRAINT:

public class MyInitializer
  : DropCreateDatabaseIfModelChanges<MyContext>
  // Or whichever base class you want to use. DropCreateDatabaseAlways<>,
  // MigrateDatabaseToLatestVersion<>, etc. or even IDatabaseInitializer<>
{
  public void InitializeDatabase(MyContext context)
  {
    // other initialization
    context.Database.ExecuteSqlCommand("ALTER TABLE Users" +
                                       "ADD CONSTRAINT UQ_Users_Username " +
                                       "UNIQUE(Username)");
  }
}

这使逻辑数据库保持绑定。您还可以实现EntityTypeConfiguration<User>并对该方法执行检查Validate,但您会在每次验证时对数据库进行 ping 操作以检查它。

于 2012-06-24T21:51:07.363 回答