我对此争论了一段时间,但仍未得出结论。虽然我看到的大多数例子都将工厂代码放在应用程序层,但我倾向于认为它应该放在域层。原因如下:有时我会在工厂里进行初步验证,我希望所有对象的创建都能通过。我希望这个代码能用于我的对象的所有实例化。有时,操作需要将感觉不自然的参数信息传递给构造函数。还有一些不那么重要的原因。
这是一种不好的做法,有什么原因吗?这打破了其他模式吗?
DDD中的工厂只是工厂模式的一个实例,因此应该在最有意义的地方使用它。另一个需要考虑的原则是信息专家模式,它本质上规定行为应该分配给最接近信息的类。因此,如果您有一些特定于域的规则和逻辑想要强制执行,请将工厂放在域层中——毕竟,工厂会创建域对象。但是请注意,在其他层中可能有其他类型的工厂。
根据记忆,Eric Evans的书中有对象工厂在很大程度上是域层的一部分的例子。
对我来说,把你们的工厂设在这里是完全合理的。
+1。可访问性是一个很好的理由,我会让创意代码至少接近领域模型层。否则,域模型的用户在查找受限访问构造函数时,会简单地弄不清如何专门实例化它。实际上,将其分离的一个合理理由是,您有不同的有效方法来创建相同的东西,例如,在使用抽象工厂时通常是这样。
如果我必须把它分开,我会把它放在至少与领域模型相同级别的包中(在Java的情况下),并总是和它一起运送,例如
upper
--> domain
--> domain_factory
我更喜欢应用层中的工厂。
如果你把工厂放在域层,当你需要复杂的类型作为参数时,它们不会帮助你(C#代码示例):
Application Layer:
//this Factory resides in the Domain Layer and cannot reference anything else outside it
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(
string name, string code, string streetName,...
and lots of other parameters...);
//these ones reside in Application Layer, thus can be much more simple and readable:
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(CreatePersonCommand);
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(PersonDTO);
Domain Layer:
public class Person : Entity<Person>
{
public Address Address {get;private set;}
public Account Account {get;private set;}
public Contact Contact {get;private set;}
public string Name {get;private set;}
public Person(string name, Address address,Account account, Contact contact)
{
//some validations & assigning values...
this.Address = address;
//and so on...
}
}
public class Address:Entity<Address>{
public string Code {get;private set;}
public string StreetName {get;private set;}
public int Number {get;private set;}
public string Complement {get;private set;}
public Address(string code, string streetName, int number, string complement?)
{
//some validations & assigning values...
code = code;
}
}
public class Account:Entity<Account>{
public int Number {get;private set;}
public Account(int number)
{
//some validations & assigning values...
this.Number = number;
}
}
//yout get the idea:
//public class Contact...
此外,没有义务将工厂保留在域层内(来自域驱动设计快速):
因此,转移创建复杂实例的责任对象和聚合到一个单独的对象,本身可能具有域模型中没有责任,但仍然是领域设计。提供一个封装所有复杂组件的接口程序集,并且不需要客户端引用正在实例化的对象的具体类。创建完整聚合为一个单元,强制执行它们的不变量。
由于我不使用工厂将持久化对象加载到内存中,因此不必从应用程序之外的其他层访问它们。原因如下(来自领域驱动设计快速):
另一个观察结果是工厂需要创建新对象从头开始,或者它们需要重建以前存在,但可能一直存在数据库将实体从静止状态带回内存数据库中的位置涉及与创建一个新的。一个明显的区别是对象不需要新标识。该对象已有一个。对不变量的违反会有不同的处理当对象是从头开始创建的,任何违反不变量的行为都将结束出现异常。我们无法对从中重新创建的对象执行此操作数据库这些物体需要以某种方式修复,所以它们可以正常工作,否则会出现数据丢失。
如果构建器/工厂只依赖于域类和基元,则将它们放置在域层中,否则将它们放置到域层之外。
小心在域层中放置"implementation"。
您的域代码没有依赖项。所以,如果你需要复杂的工厂,你就有麻烦了。
例如:
// DOMAIN LAYER
public interface IAggregateFactory<TAgg, in TInput>
{
Task<TAgg> CreateAsync(TInput input);
}
public class AvailabilityFactoryParameters
{
public string SomeInputParameter { get; set; }
public string ZipCode { get; set; }
}
// INFRASTRUCTURE/APPLICATION LAYER
public class AvailabilityFactory : IAggregateFactory<GamePredictorAggregate,
GamePredictorFactoryParameters>
{
private readonly HttpClient _httpClient;
public AvailabilityFactory(IHttpClientFactory factory)
{
_httpClient = factory.CreateClient("weatherApi");
}
public async Task<GamePredictorAggregate> CreateAsync(GamePredictorFactoryParameters input)
{
var weather = await _httpClient.GetFromJsonAsync<WeatherDto>($"/weather/{input.ZipCode}");
return new GamePredictorAggregate(weather.CurrentTemperature, input.SomeInputParameter);
}
}
public class WeatherDto
{
public double CurrentTemperature { get; set; }
}
正如您所看到的,现在您有无数的对象和依赖项可用于丰富您的工厂体验。
因此,当您在应用程序服务中使用它时,它很容易。。。
public class GamePredictionService : ApplicationService
{
private readonly IAggregateFactory<GamePredictorAggregate, GamePredictorFactoryParameters> _factory;
public GamePredictionService(IAggregateFactory<GamePredictorAggregate, GamePredictorFactoryParameters> factory)
{
_factory = factory;
}
public async Task CreateNewPredictor(string zipCode, int someOtherParamater)
{
var input = new GamePredictorFactoryParameters();
input.ZipCode = zipCode;
input.SomeInputParameter = someOtherParamater;
var aggregate = await _factory.CreateAsync(input);
// Do your biz operations
// Persist using repository
}
}
现在,您的应用程序服务不需要担心内部,您的域对象需要了解工厂是如何让它们"诞生"的
摘要:只有当您的工厂只需要基元类型而不需要其他类型时,在Domain层中实现才有意义。如果您可能需要从外部服务或其他应用程序服务的DTO收集数据,则需要将实现转移到外部。唯一的"缺点"是需要将工厂"注入"到应用程序服务中,但这并不是什么大不了的事。
我希望这个答案有助于澄清工厂的位置