贫血领域模型或“逻辑放在哪里”
这是“分析瘫痪”似乎已经发生的场景之一,所以请提出建议!
该项目
一个相当简单的汽车产品列表,其中包括零件参考、适合哪些车辆等详细信息。
前端是一个 asp.net MVC 应用程序。
后端是 SQL,使用 Subsonic 将产品投影到域对象中。
功能
我们的屏幕之一是产品详细信息屏幕。 ASP.NET MVC 控制器调用产品存储库来检索产品详细信息,并将这些详细信息(通过某些自动映射到 viewModel)返回到视图。
现在最致命的细节是我们有两个或三个进入网站的渠道,根据渠道的不同,用户需要看到不同的零件号。
举例来说,如果是零售渠道,则零件编号与数据库中的编号相同,但如果用户通过贸易渠道访问该网站,则零件参考的开头将被替换为替代编号。
例如,如果通过贸易渠道查看,0900876 就会变成 1700876。
我正在努力决定在哪里封装有关零件参考(以及可能改变的其他细节)的“渠道规则”。
我已经考虑过这些替代方案。
将逻辑直接写入域对象
在Product类上,我们可以有一个方法/属性来获取翻译后的部件引用。
public string TranslatedPartRef()
{
if (this.Channel == "Trade")
{
return PartRef.Replace("0900", "1700");
}
else
{
return PartRef;
}
}
在这种情况下,产品实例必须了解通道,这对我来说似乎是错误的。
将逻辑封装在另一个对象中
我们可以编写一个类来处理这部分引用转换,或者创建一个包含此逻辑的 Channel 类。
但我不明白的是如何协调这两个类。
如果控制器调用存储库来检索产品,那么它是否应该找出使用的通道并翻译零件参考?如果是这样,我该如何将产品及其翻译后的零件参考发送回视图?
还值得注意的是,这部分参考也必须显示在搜索结果和其他场景中,因此我认为它需要整齐地包含在域中的某个位置。
This is one of those scenarios where "Paralysis by Analysis" seems to have taken hold so advice please!
The project
A fairly simple list of automotive products which include details such as part reference, which vehicles they fit etc.
The front end is an asp.net MVC application.
The backend is SQL, using Subsonic to project the products into domain objects.
Functionality
One of our screens is a product details screen. An ASP.NET MVC Controller calls the product repository to retrieve product details, returns these details (via some automapping to a viewModel) to the view.
Now the killer detail is that we have two or three channels into the web site, depending on the channel, the user needs to see different part numbers.
Lets say for example if it's the Retail channel then the part numbers are as they are in the database, but if the user has come to the site through the Trade channel, the beginning of the part reference is replaced with alternative numbers.
e.g. 0900876 if viewed via the Trade channel becomes 1700876.
Where I'm struggling is in deciding where to encapsulate the "Channel rules" regarding part references (and other details which may alter).
I've considered these alternatives.
Write the logic directly into the domain object
On the Product class we could have a method/property to get the translated part reference.
public string TranslatedPartRef()
{
if (this.Channel == "Trade")
{
return PartRef.Replace("0900", "1700");
}
else
{
return PartRef;
}
}
In this scenario the Product instance must know about the channel, which seems wrong to me.
Encapsulate the logic in another object
We could write a class to handle this part reference translation, or create a Channel class which contains this logic.
What I don't understand though is how to then co-ordinate the two classes.
If the controller calls the repository to retrieve the Product, should it then work out what channel was used and translate the part reference? if so how do I then send the product with it's translated part reference back to the view?
It's also worth noting that this part reference has to show up in search results and other scenarios as well, for that reason I think it needs to be neatly contained within the domain somewhere.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我不是 C# 人员,但我想我会用 Java 中的装饰器来解决这个问题。
假设您有一个产品接口,那么您可以创建一个装饰器来管理部件号问题。
I'm not a C# guy, but I would attack this with a Decorator in Java, I think.
Assuming you have an interface for Product, then you can create a Decorator which manages the part number issue.
您需要问自己的第一个问题是 Channel 的概念是否是领域概念。您的问题似乎表明事实并非如此,但另一方面,我认为这听起来也不是特定于应用程序的。
您可能会问的另一个问题是:如果将来我需要在此域模型之上构建另一个应用程序(例如 Web 服务或富客户端),我是否仍然需要处理以下概念:渠道?
我的猜测是答案可能是是。
据我了解您的问题,频道以某种方式与请求上下文相关。也许这确实是用户的一个属性。或者也许 in 是应用程序配置本身的一个属性。
无论如何,我都会认真思考它是否真的不是一个领域概念。如果是,那么它可以很好地属于域对象。
如果没有,ptomli 建议的装饰器实现听起来是一个不错的方法。
The first question you need to ask yourself is whether the concept of a Channel is a domain concept or not. Your question seems to indicate that it isn't, but on the other hand, I don't think it sounds application-specific either.
An additional question you could ask is: if, in the future, I need to build another application on top of this domain model (e.g. a web service or a rich client), would I still need to deal with the concept of a Channel?
My guess is that the answer might be yes.
As far as I understand your question, the Channel is related to the request context in some way. Perhaps it is really an attribute of the user. Or perhaps in is an attribute of the application configuration itself.
In any case, I would think hard about whether it isn't really a Domain concept after all. If it is, then it could belong nicely in a Domain Object.
If not, the Decorator implementation suggested by ptomli sounds like a good approach.
零件号会有多少种不同的变体。如果只是贸易与零售,我很想简单地将两个数字都放在 Product 对象中,并让 UI 决定显示哪个。当对产品进行操作时,身份可以是“类型{贸易,零售},数字”。
对于模式灵活的东西,我认为你的频道想法很好。但如果它有双向责任,将零售与贸易进行映射,这似乎会起作用。 Channel 对象可以被视为一个适配器,能够进行其他转换和丰富。
作为一个实现,我将为每个 Channel 创建一个单独的 Channel 对象,试图避免 case 语句和 if else else 逻辑。对于零售业,渠道对象可以是一个 NOOP 对象,对于贸易业,它可以进行映射。工厂可以创建适当的 Channel 对象。
How many different varients of part number would there be. If it's just Trade v Retail i'd be very tempted to simply have both numbers in the Product object, and have the UI decide which to display. When acting on product the identity can be "type{Trade, Retail}, number".
For something mode flexible I think that's your Channel idea is fine. But if it had bi-directional responsibilities, mapping retail to and from trade, this would seem to work. The Channel object con be seen as an adapter, capable of other transforms and enrichments.
As an implementation I would be creating a separate Channel object for each Channel, trying to avoid case statements and if else else logic. For Retail the Channel object could be a NOOP object for Trade it can do teh mappings. A factory can creat the approporaye Channel object.
如果零件编号映射可能发生变化怎么办?现在它是一个会发生变化的前缀,但是您是否还需要满足其他类型的变化?也许您不需要这个,但是:
在业务层面,您说产品可以有不同的部件号,具体取决于渠道(这毕竟是一个基本的业务概念)。因此,这表明在数据库级别,某处可能存在一个 PartNumber 表,其中包含 ProductId、ChannelId 和 PartNumber 列。这肯定会涵盖随着时间的推移出现更多渠道的情况(今天是零售或贸易,明天他们可能会添加网络、邮购等。可以想象,所有这些都可能需要不同的部件号)。
在对象级别,这映射到具有
Dictionary
的Product
实例,可用于在给定Channel< 的情况下获取适当的部件号/代码>。
What if the part number mapping could change? Right now it's a prefix which changes, but could there be other types of changes you have to cater for? Maybe you don't need this, but:
At the business level, you're saying that a product can have different part numbers, dependent on channel (which is after all a fundamental business concept). So that suggests that at the database level, there could be a PartNumber table somewhere which has ProductId, ChannelId and PartNumber columns. This will certainly cover the case where more channels appear over time (today it's Retail or Trade, tomorrow they might add Web, Mail-Order etc. all of which conceivably could want different part numbers).
At the object level, this maps to a
Product
instance having aDictionary<Channel, PartNumber>
which can be used to get the appropriate part number given aChannel
.一个简单的解决方案:
没有装饰器,没有开关,没有控制反转。
每次您需要特定渠道的零件号时,您都可以调用此方法。
每次您需要不同的零件编号计算方法时,您只需实现此接口即可。
A straight forward solution:
No decorators, no switches, no inversion of control.
Every time you need part number for a specific channel you call this method.
Every time you need a different part number calculation method you simply implement this interface.