DDD删除聚合的最佳解决方案
DDD Best solution to delete aggregate
在 DDD 中我有一个聚合 Lobby
public class Lobby : Entity
{
public List<User> Users { get; private set; }
public void RemoveUser(string userId)
{
var user = Users.First(e => e.Id == userId);
Users.Remove(user);
if(Users.Count == 0)
{
// REMOVE THIS LOBBY
}
}
}
和应用服务
public class LobbyService : ILobbyService
{
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
}
}
删除该大厅的最佳方法是什么?抛出异常/返回特定值或域事件?然而,前一种解决方案意味着我的应用程序服务将使用一堆 if 语句(我认为这很糟糕?),而后者将意味着将有两次数据库往返(从事件处理程序中删除,然后从应用程序服务更新,因为 dbContext不会分享)
我是 DDD 的新手,但我还不是很清楚。
我个人仍然会使用 if statement
来检查 lobby
是否为 null。
public class LobbyService : ILobbyService
{
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
if (lobby == null)
throw new ArgumentNullException("Lobby is null"); // or return Result.Error("Lobby is null");
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
UserRemoved(lobbyId, userId);
}
}
"Don't Delete, Just Don't" -- Udi Dahan, 2009
就是说,如果您要删除某些内容...
存储库的操作通常发生在应用程序组件中;换句话说,当实体处于特定状态时,您想调用 delete
而不是保存。
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
if(ShouldDelete(lobby)) {
_lobbyRepository.delete(lobbyId)
}
_lobbyRepository.SaveChanges();
}
boolean ShouldDelete(Lobby lobby)
{
return ????
}
the former solution would mean my Application service would use a bunch of if statements (which I think is bad?)
不,这还不错 - 这不是常见情况。
关键思想:应用逻辑和领域逻辑是不同的动物。应用逻辑属于应用程序,领域逻辑属于领域模型。 “我们应该使用哪个 I/O 操作?”是应用程序问题,而不是领域问题。
更常见的方法是将删除安排在以后发生(异步)。有很多不同的方法可以做到这一点——最简单的是让领域实体记录它自己的生命终结,然后在稍后的某个时间 运行 一个实际执行回收过程的“垃圾收集器”未使用的 space.
在那种设计中,您的应用程序代码看起来更像您期望的那样:
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
}
异步垃圾收集器类似于
DELETE FROM lobby WHERE lobby.should_delete
should_delete
可能是一个布尔值,或者它可能是一个时间戳,用于与 now()
或某个目标时间等进行比较。许多可能性,不同的权衡。
您可以使用消息传递与垃圾收集器通信 - 您会得到一组不同的权衡。消息是 I/O 的另一种形式,因此应用程序将对它们有所了解 - 但它可能是一种非常通用且可重复使用的机制。
同样,许多权衡 - 消息是持久的还是短暂的?是数据库事务的一部分?我们的消息传输解决方案有多可靠,我们是否需要备份机制来确保即使删除消息丢失也能删除大厅?
在 DDD 中我有一个聚合 Lobby
public class Lobby : Entity
{
public List<User> Users { get; private set; }
public void RemoveUser(string userId)
{
var user = Users.First(e => e.Id == userId);
Users.Remove(user);
if(Users.Count == 0)
{
// REMOVE THIS LOBBY
}
}
}
和应用服务
public class LobbyService : ILobbyService
{
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
}
}
删除该大厅的最佳方法是什么?抛出异常/返回特定值或域事件?然而,前一种解决方案意味着我的应用程序服务将使用一堆 if 语句(我认为这很糟糕?),而后者将意味着将有两次数据库往返(从事件处理程序中删除,然后从应用程序服务更新,因为 dbContext不会分享) 我是 DDD 的新手,但我还不是很清楚。
我个人仍然会使用 if statement
来检查 lobby
是否为 null。
public class LobbyService : ILobbyService
{
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
if (lobby == null)
throw new ArgumentNullException("Lobby is null"); // or return Result.Error("Lobby is null");
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
UserRemoved(lobbyId, userId);
}
}
"Don't Delete, Just Don't" -- Udi Dahan, 2009
就是说,如果您要删除某些内容...
存储库的操作通常发生在应用程序组件中;换句话说,当实体处于特定状态时,您想调用 delete
而不是保存。
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
if(ShouldDelete(lobby)) {
_lobbyRepository.delete(lobbyId)
}
_lobbyRepository.SaveChanges();
}
boolean ShouldDelete(Lobby lobby)
{
return ????
}
the former solution would mean my Application service would use a bunch of if statements (which I think is bad?)
不,这还不错 - 这不是常见情况。
关键思想:应用逻辑和领域逻辑是不同的动物。应用逻辑属于应用程序,领域逻辑属于领域模型。 “我们应该使用哪个 I/O 操作?”是应用程序问题,而不是领域问题。
更常见的方法是将删除安排在以后发生(异步)。有很多不同的方法可以做到这一点——最简单的是让领域实体记录它自己的生命终结,然后在稍后的某个时间 运行 一个实际执行回收过程的“垃圾收集器”未使用的 space.
在那种设计中,您的应用程序代码看起来更像您期望的那样:
public void RemoveUser(string lobbyId, string userId)
{
var lobby = _lobbyRepository.GetById(lobbyId);
lobby.RemoveUser(userId);
_lobbyRepository.SaveChanges();
}
异步垃圾收集器类似于
DELETE FROM lobby WHERE lobby.should_delete
should_delete
可能是一个布尔值,或者它可能是一个时间戳,用于与 now()
或某个目标时间等进行比较。许多可能性,不同的权衡。
您可以使用消息传递与垃圾收集器通信 - 您会得到一组不同的权衡。消息是 I/O 的另一种形式,因此应用程序将对它们有所了解 - 但它可能是一种非常通用且可重复使用的机制。
同样,许多权衡 - 消息是持久的还是短暂的?是数据库事务的一部分?我们的消息传输解决方案有多可靠,我们是否需要备份机制来确保即使删除消息丢失也能删除大厅?