我正在尝试使用DDD建模一个简单的域。数据库层使用Entity Framework实现,域对象是POCO。域具有User实体,该实体具有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或类似的东西吗?
答案 0 :(得分: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数据库以进行检查。
答案 1 :(得分:1)
db是该规则的最终执行者。现在,这是一个多用户应用程序?如果是这样,那么最实用的方法是依赖db,捕获sql异常,然后抛出一个将重定向到UI的businessrule异常(即会触发错误消息)。
您可以验证域级别上是否已存在名称(通过服务是最有效的方式),但这可能会在并发环境中失败。但是,对于单个用户应用程序,它是一个优雅而干净的解决方案。通过单用户应用程序,我的意思是所有应用程序将一次为一个用户服务。如果您有WPF客户端和Web服务,那就是多用户应用程序。
最好的办法是让持久性(在本例中为db)处理此规则,因为repo有责任确保没有重复的名称(存储库不是一个笨拙的桶,它是持久性的管理者)它也解决了并发问题。
请注意,我没有提到EF,因为与db通信的方式无关紧要。如果明天你将切换到Azure数据库,解决方案仍然是相同的,只有实现的具体部分会有所不同。