ASP.NET MVC:这个业务逻辑应该放在哪里?

4
我正在开发我的第一个真正的MVC应用程序,并尝试遵循一般的OOP最佳实践。我正在将一些简单的业务逻辑从控制器中重构到我的领域模型中。最近我一直在阅读,似乎很清楚我应该将逻辑放在领域模型实体类中的某个地方,以避免“贫血的领域模型”反模式。
该应用程序将允许人们购买停车位租赁。费率由停车位长度和客户是否是商业园区会员决定。
因此,我的领域模型中有像这样的实体类(简化):
public class Customer
{
    int ID { get; set; }
    string Name { get; set; }
    bool IsMember { get; set; }
}

public class ParkingSpace
{
    int ID { get; set; }
    int Length { get; set; }
}

public class ParkingSpaceLease
{
    int ID { get; set; }
    DateTime OpenDate { get; set; }
    DateTime CloseDate { get; set; }
    Customer Customer { get; set; }
    ParkingSpace ParkingSpace { get; set; } 
}

编辑:仅为澄清,LeaseQuote不是实体类,它仅用于向潜在客户展示成本细分,并且不会在任何地方持久存在。

public class LeaseQuote
{
    int SubTotal { get; set; }
    int Discount { get; set; }
    int Total { get; set; }
}

现在作为应用程序的一个功能,我需要能够针对不同的客户和停车位组合生成报价。通常情况下,报价将在实际创建租赁之外访问,例如当客户打电话询问价格时。
那么,最佳方法是什么?是否有意义在控制器内实例化一个新的ParkingSpaceLease对象,仅仅为了调用它的GetQuote方法?
var lease = new ParkingSpaceLease();
var quote = lease.GetQuote(length: 168, isMember: true);
return Json(quote);

或者LeaseQuote类应该有这个方法吗?
var leaseQuote = new LeaseQuote();
var quote = leaseQuote.GetQuote(length: 168, isMember: true);
return Json(quote);

将逻辑放在实际的ParkingSpaceLease类中感觉有些奇怪。我想这种做法可能会让人感到“沉重”,因为我知道我创建一个新的租约对象时,除了访问GetQuote方法之外,不会做任何其他事情,而这个方法似乎像是一个独立的服务。

那么GetQuote方法应该放在哪里?为什么要放在那里?

3个回答

4

你的LeaseQuote似乎更像是一个业务级别的类,而不是一个实体。我的意思是,你没有在数据库中存储它,对吗?它也不是其他数据对象的一部分。

当我看到这个时候

现在作为应用程序的一个功能,我需要能够为不同的客户和停车位组合生成报价。这些报价通常会在实际创建租赁之外的上下文中访问,例如当客户打电话询问价格时。

我会想到一个方法签名,像这样

public LeaseQuote GetQuote(Customer customer, ParkingSpace parkingSpace, int length)

但是考虑到这一点,我可能还想将停车位的成本信息存储在ParkingSpace实体中,并在Customer实体中存储(如果适用)客户的折扣信息。
这些东西应该放在哪里?在一个模型类(业务模型,而不是LINQ或Entity模型)中,该模型类访问您的实体并作为控制器的提供程序。
现在我知道这不是按照你写的模型正好使用。它可能只是个人偏见。但是,当我思考数据模型和数据实体时,它们不应该有任何超出从数据库返回的内容之外的附加方法。他们应该只表示数据,就像在数据库中一样没有改变。如果你正在操作数据,则属于数据实体以上的层级。
更新:
引用:
“我很好奇你的例子中,为什么要传递完整的实体对象(Customer和ParkingSpace),而不是执行计算所需的属性?”
这取决于您的代码标准。如果使用方代码操作实体本身可能很危险。我更喜欢传递实体,因为我习惯于这样做。但是,我也小心在入口处不要操作实体。此外,我认为方法签名反映了GetQuote方法关注的内容;它与客户和停车位有关。
我还可以说,如果以后更多的字段进入Entity可能会影响GetQuote方法,那么方法签名就不必更改。在这种情况下,只需要更改GetQuote的实现。
简短回答:偏好。

你关于LeaseQuote不是实体的说法是正确的。我应该把这个问题讲清楚。一旦租赁合同真正创建,购买、折扣信用和付款信息都将反映在交易中,因此LeaseQuote的目的只是为了向潜在客户展示细节。从你的例子中,我很好奇为什么要传递完整的实体对象(客户和停车位),而不是只传递执行计算所需的属性? - Jeff Camera
好的 :) 所以,把计算放在另一个模型类中(也许是LeaseManager,它还可以有添加新租约、删除租约、更新租约等方法),或者如果您认为它不会在其他地方使用,就将其放在控制器中。 - villecoder
你能否回答一下我的问题,关于方法签名接受实体对象而不是属性值?我在你回复之前编辑了这个问题! - Jeff Camera

0

我认为您的对象模型可能有些不正确,这会导致您担心租约不是获取报价的正确位置。在我看来,租赁应该完全由被租赁的停车位组成,并且只与购买租赁的客户相关。换句话说:

public class ParkingSpace
{
    int ID { get; set; }
    int Length { get; set; }
    IEnumerable<ParkingSpaceLease> Leases { get; set; }
    LeaseQuote GetQuote(Customer customer/*, other relevant parameters */) { ... }
}

public class ParkingSpaceLease
{
    int ID { get; set; }
    DateTime OpenDate { get; set; }
    DateTime CloseDate { get; set; }
    Customer Customer { get; set; }
}

public class LeaseQuote
{
    //Properties
    ParkingSpaceLease GetLease();
}

编辑 我错过了LeaseQuote是一个单独的类的部分。


0

只需要将GetQuote设置为ParkingSpaceLease中的静态方法即可。


这是“别担心”的回复吗? - jball

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接