【问题标题】:Where should I place business logic when using RavenDB使用 RavenDB 时应该在哪里放置业务逻辑
【发布时间】:2013-07-10 01:00:21
【问题描述】:

我正计划使用 RavenDB 作为我的数据存储构建单页应用程序 (SPA)。

我想从 SPA 作品的 ASP.NET 热毛巾模板开始。

我将删除 EntityFramework/WebApi/Breeze 组件并替换为 RavenDB 用于存储和 ServiceStack 用于构建后端 API。

目前大多数观点似乎不赞成在 RavenDB 之上使用任何类型的存储库或额外的抽象,并呼吁直接在控制器内部(在 MVC 应用程序中)使用 RavenDB API

我假设在将 Raven 与 ServiceStack 一起使用并直接在我的服务实现内部调用 IDocumentSession 时,我应该遵循同样的原则。

我担心的是,我的服务实现似乎会因为遵循这条路径而变得相当臃肿。似乎我经常需要多次编写相同的代码,例如,如果我需要在几个不同的 Web 服务端点中更新用户文档。

我似乎还需要从我的应用程序的其他(未来)部分访问 Raven。例如,我将来可能需要添加一个控制台应用程序来处理队列中的作业,而这部分应用程序可能需要访问 Raven 中的数据……但从一开始,我通往 Raven 的唯一途径就是通过网络服务 API。我只是打算从这个理论上的控制台应用程序调用 web api 吗?如果它们可能在相同的硬件上运行,似乎效率低下。

谁能就如何在我的网络服务和其他地方有效利用 Raven 提供任何建议,同时在使用此文档存储时仍遵循最佳实践?创建一个中间业务逻辑层来直接处理对 raven 的调用似乎很实用……允许我的 web 服务调用该层内的方法。这有意义吗?

编辑

谁能提供最近类似架构的样本?

【问题讨论】:

    标签: c# .net nosql servicestack ravendb


    【解决方案1】:

    FWIW,我们目前正在开发使用 ServiceStack 和 RavenDB 的应用程序。我们正在使用 DDD 方法,并将我们的业务逻辑放在丰富的领域层中。架构是:

    1. Web 应用程序。托管 Web 客户端代码 (SPA) 和服务层。

    2. 服务层。 Web 服务使用 ServiceStack 和干净/相当扁平的 DTO,这些 DTO 与域对象完全解耦。 Web 服务负责管理事务和所有 RavenDB 交互。大多数“命令式”服务操作包括:a)加载由请求标识的域对象(文档),b)调用业务逻辑,c)将结果转换为响应 DTO。我们增强了 ServiceStack,以便许多命令式操作使用自动处理程序,无需任何代码即可完成上述所有操作。 'Query-ish' 服务操作通常包括:a)对 RavenDB 执行查询,b)将查询结果转换为响应 DTO(实际上这通常作为 a 的一部分完成),在查询处理期间使用 RavenDB/指数/变压器)。业务逻辑总是下推到领域层。

    3. 域层。与 DDD 语言中的“根聚合”相对应的文档完全与数据库无关。他们对如何加载/保存等一无所知。域对象仅公开公共 GETTER 和私有 SETTER。修改域对象状态的唯一方法是调用方法。域对象公开旨在由服务层使用的公共方法,或在域层内使用的受保护/内部方法。域层引用 Messages 程序集,主要是为了允许域对象上的方法接受复杂的请求对象,并避免使用参数列表过长的方法。

    4. 消息程序集。支持其他本地 .Net 客户端的独立程序集,例如单元测试和集成测试。

    对于其他客户,我们有两种选择。我们可以引用 ServiceStack.Common 和 Messages 程序集并调用 Web 服务。或者,如果需求大不相同并且我们希望绕过 Web 服务,我们可以创建一个新的客户端应用程序,引用域层程序集和 Raven 客户端并直接以这种方式工作。

    在我看来,存储库模式是一种不必要且有漏洞的抽象。我们仍在开发中,但到目前为止,上述内容似乎运行良好。

    编辑

    一个大大简化的域对象可能看起来像这样。

    public class Order
    {
        public string Id { get; private set; }
        public DateTime Raised { get; private set; }
        public Money TotalValue { get; private set; }
        public Money TotalTax { get; private set; }
        public List<OrderItem> Items { get; private set; }
    
        // Available to the service layer.
        public Order(Messages.CreateOrder request, IOrderNumberGenerator numberGenerator, ITaxCalculator taxCalculator)
        {
            Raised = DateTime.UtcNow;
            Id = numberGenerator.Generate();
            Items = new List<OrderItem>();
            foreach(var item in request.InitialItems)
                AddOrderItem(item);
            UpdateTotals(taxCalculator);
        }
    
        private void AddOrderItemCore(Messages.AddOrderItem request)
        {
            Items.Add(new OrderItem(this, request));
        }
    
        // Available to the service layer.
        public void AddOrderItem(Messages.AddOrderItem request, ITaxCalculator taxCalculator)
        {
            AddOrderItemCore(request);
            UpdateTotals(taxCalculator);
        }
    
        private void UpdateTotals(ITaxCalculator taxCalculator)
        {
            TotalTax = Items.Sum(x => taxCalculator.Calculate(this, x));
            TotalValue = Items.Sum(x => x.Value);
        }
    }
    

    【讨论】:

    • 感谢您的详细解答!我研究得越多,我就越倾向于您所描述的架构。您能否提供一个典型的域对象希望使用设置属性的方法的示例?
    【解决方案2】:

    这里有两个主要部分需要考虑。

    首先,正如您已经指出的那样,如果您按照更狂热的 RavenDB 粉丝的话来说,它是一些神话般的野兽,它不受其他普遍接受的良好应用程序设计法则的约束,应该被允许渗透到您的应用程序中随意。

    这当然取决于具体情况,但简单地说,如果您要使用 SQL Server 之类的某种方式构建应用程序,那么对 RavenDB 也一样。如果你有一个 DAL 层、ORM、存储库模式或任何带有 SQL Server 后端的东西,那么对 RavenDB 做同样的事情。如果您不介意抽象泄漏,或者项目小到根本不需要抽象您的数据访问,请相应地编写代码。

    与 RavenDB 的主要区别在于您获得了一些东西,例如工作单元和“免费”的 ORM,但整体解决方案架构不应该有那么不同。

    第二,连接其他客户端。为什么控制台应用程序(或任何其他客户端)会以与您的网站不同的方式访问您的 RavenDB 服务器实例?即使您在 ASP.NET 应用程序中运行服务器嵌入模式,您仍然可以使用相同的 RavenDB.Client 代码将其他客户端连接到它。您不需要直接接触 Web 服务 API。

    【讨论】:

    • 是的,从另一个客户端调用 RavenDB 应该与 Web 服务没有任何不同。我的错误,这是一个糟糕的例子。
    猜你喜欢
    • 2016-12-18
    • 2012-07-16
    • 2011-08-02
    • 2017-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    相关资源
    最近更新 更多