我正在使用域驱动设计方法开发Web应用程序,但是我的应用程序的某些方面与DDD不太匹配。例如,我们需要对员工工资进行批量更新。加载整个员工实体并为每个员工保存它是无效的,因为通常会同时更新数百个工资。此外,还有其他任务需要同时执行,例如记录旧工资和记录新工资的生效日期。所以我会说这种类型的操作超出了我的核心域的有限上下文。此外,我理解这个操作最好从程序角度来处理。这一切都很好,但我仍然希望我的应用程序能够连贯和有条理,无论我在我的应用程序的特定部分使用什么方法。
例如我使用以下结构。
我想坚持这个结构,即使是在核心域的有限上下文之外的事情。我目前关注的主要是我的基础设施层。最初我在基础设施中使用以下内容:
我在Finders中使用ad-hoc读取查询,在命令中使用ad-hoc命令。这样做的问题在于某些操作需要一系列查询和命令,而且我似乎更有条理地将它们组合在一个单元中。有点像存储库,但它不是提供对域实体的访问,而是封装构成特定过程的一组查询和命令。
所以我正在寻找的是建议重新命名将“Commands”文件夹/命名空间更改为更好地描述逻辑上适合的一系列查询/命令的约定。是否已有一个我不知道的名称/模式?
更新
目前正在考虑名称空间“过程”来描述逻辑上适合的这些查询/命令。一方面它是合适的,因为我所描述的与存储过程类似,并且描述了应用程序的这一部分使用过程而不是DDD方法。我唯一的疑惑是这个命名约定意味着使用存储过程,而事实并非如此。
答案 0 :(得分:3)
阅读此article first它可以帮助您进行批量更新。 IMO Salary不是员工定义的一部分,我会说Salary是关联的还是需要一个员工(作为c的参考)。
更改薪水是域用例,保持旧薪水似乎是域规则。他们确实希望保留所有旧工资吗?它不是技术决策,而是业务决策。就个人而言,我并不认为这是程序性思维的理由。
关于执行需要查询和命令的操作。命令是改变域的东西。你说那些命令/查询构成了一个特定的过程。什么?看来你正在描述一个用例。这是领域已知的东西吗?它需要业务逻辑吗?或者严格持久性或基础架构相关(更新一些读取模型,查询其他读取模型)?如果有逻辑,则表示您可能拥有服务。如果它的业务逻辑可能是业务概念或用例,那么它们属于域。如果它的持久性逻辑属于DAL,如果它是UI逻辑,那么它属于UI等。
更新 评论时间过长
文章的重点是,如果你需要Domain实体上的大量内容,那么在99%的情况下你使用的是错误的模型。在您的情况下,员工和工资之间的关系确实很重要。我会说(只是一个非常模糊的建议)你可以有一个SalariesManager(坏名称......),它会将员工(id)与特定日期开始的薪水相关联。
public interface IManageSalaries
{
void ChangeEmployeeSalary(Guid employee, Salary newSalary,DateTime startingFrom);
//possible bulk operation
void ChangeSalary(Guid[] employees, Salary newSalary,DateTime startingFrom);
SalaryInfo[] GetSalaryHistory(Guid employeeId);
Salary GetCurrentSalary(Guid employeeId);
}
实现只会向表中添加一个新的(employee_id,salary,date)。您更改当前工资但保留旧条目。接口可以是Domain的一部分,但实现是Persistence的一部分(因为它很简单,并且它不包含业务逻辑)。想想看,它看起来像一个存储库,但没有聚合根。
但这只是一个建议。我觉得还有另一种解决方案可以更好地适应Domain。由您和领域专家来识别它。
答案 1 :(得分:0)
应用程序责任中的命令/查询,请查看六边形体系结构。您可以将ad-hoc命令和查询作为适配器/端口添加到应用程序
答案 2 :(得分:0)
我为我的存储库提供了开始事务的能力,然后将TransactionCommand传递给所有涉及的存储库。最终存储库完成更新后,我提交了它。这样,我仍然可以使用我现有的存储库(基本上每个模型/实体一个),而无需编写ad-hoc /自定义查询。
我只是希望我的模型/实体本身实现存储库接口,而不是为每个接口定义一个单独的存储库。前者允许多态存储库操作,后者则不允许。生活和燃烧。
以下是事务存储库的用法,此方法取自Entity1Service类(我的服务层,每个实体一个类,包含使用一个或多个存储库完成某些目标的静态方法)
public class Entity1Service
{
public static bool WriteEntity1(Entity1 entity1)
{
Entity1RepositorySQLite sqlRepo = new Entity1RepositorySQLite();
sqlRepo.BeginTransaction();
int entity1Id;
if (!LocalIdService.ReadAndIncrementEntity1Id(out entity1Id, sqlRepo.TransactionCommand))
{
sqlRepo.RollbackTransaction();
return false;
}
int entity1IdOld = entity1.Id;
try
{
IdMapService.CreateMapEntity1Id(entity1Id, entity1Id, sqlRepo.TransactionCommand);
entity1.Id = entity1Id;
sqlRepo.Write(attachment);
}
catch (Exception)
{
sqlRepo.RollbackTransaction();
attachment.Entity1Id = attachmentIdOld;
throw;
}
sqlRepo.EndTransaction();
return true;
}
}
存储库定义
public interface ITransaction
{
SqliteCommand TransactionCommand { get; set; }
bool InTransaction { get; }
void BeginTransaction();
void RollbackTransaction();
void CloseTransaction();
void EndTransaction();
}
public class RepositorySQLiteTransactional<T> : RepositorySQLite<T>, ITransaction where T : new()
{
public RepositorySQLiteTransactional(IDataMapperSQLite<T, string[]> dataMapper) : base(dataMapper) { }
public RepositorySQLiteTransactional(IDataMapperSQLite<T, string[]> dataMapper, SqliteCommand transactionCommand) : this(dataMapper)
{
_dbLayer.DblTransactionCommand = transactionCommand;
}
public SqliteCommand TransactionCommand
{
get { return (SqliteCommand)_dbLayer.DblTransactionCommand; }
set
{
_dbLayer.DblTransactionCommand = value;
}
}
public bool InTransaction
{
get { return _dbLayer.DblInTransaction; }
}
public void BeginTransaction()
{
try
{
_dbLayer.DblBeginTransaction();
}
catch( Exception )
{
_dbLayer.DblCloseTransaction();
throw;
}
}
public void RollbackTransaction()
{
_dbLayer.DblRollbackTransaction();
}
public void CloseTransaction()
{
_dbLayer.DblCloseTransaction();
}
public void EndTransaction()
{
_dbLayer.DblEndTransaction();
}
}
public interface IRepository<T>
{
List<T> Read(IConditions conditions);
T FindOne(IQuery query);
List<T> FindAll(IQuery query);
void WriteOne(T obj);
void WriteOne(T obj, out int newId);
void WriteOne(IQuery query);
void WriteAll(List<T> objs);
void UpdateOne(T obj);
void UpdateAll(List<T> objs);
void UpdateOne(IQuery query);
void ReplaceAll(List<T> objs);
void DeleteAll();
void DeleteAll(List<T> objs);
//void Add(T entity);
//void Delete(T entity);
//void Edit(T entity);
//void Save();
}
public class RepositorySQLite<T> : IRepository<T> where T : new()
{
protected AndroidDB _dbLayer;
protected IDataMapperSQLite<T, string[]> _dataMapper;
private RepositorySQLite() // force data mapper init
{
}
public RepositorySQLite(IDataMapperSQLite<T, string[]> dataMapper)
{
}
public List<T> Read(IConditions conditions) { throw new NotImplementedException(); }
public void WriteOne(T obj, out int newId) { throw new NotImplementedException(); }
public void WriteOne(IQuery query) { throw new NotImplementedException(); }
private void ClearMapState()
{
}
public void ReplaceAll(List<T> objs)
{
}
public void WriteAll(List<T> objList)
{
}
public void WriteOne(T obj)
{
}
public void UpdateOne(T obj)
{
}
public void UpdateAll(List<T> objs)
{
}
public void UpdateOne(IQuery query)
{
}
public T FindOne(IQuery query)
{
}
public List<T> FindAll(IQuery query)
{
}
public void DeleteAll(List<T> objs)
{
}
public void DeleteAll()
{
}
public void DeleteAll( IQuery query )
{
}
}