使用RavenDB时,我应该在哪里放置业务逻辑

时间:2013-07-10 01:00:22

标签: c# .net nosql servicestack ravendb

我打算使用RavenDB作为我的数据存储来构建单页面应用程序(SPA)。

我想从SPA片的ASP.NET Hot Towel模板开始。

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

目前大多数意见似乎都不赞成在RavenDB上使用任何类型的存储库或其他抽象,并且要求直接在控制器内部使用RavenDB API(在MVC应用程序中)

我假设我在使用Raven和ServiceStack时应该遵循同样的智慧,并直接在我的服务实现中对IDocumentSession进行调用。

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

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

任何人都可以提供有关如何在我的网络服务和其他地方有效利用Raven的建议,同时在使用此文档存储时仍遵循最佳做法?创建一个直接处理针对raven的调用的中间业务逻辑层似乎很实际......允许我的webservices调用此层中的方法。这有意义吗?

修改

任何人都可以提供类似架构的最新样本吗?

2 个答案:

答案 0 :(得分:7)

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

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

  2. 服务层。使用ServiceStack的Web服务,其中干净/相当扁平的DTO与Domain对象完全分离。 Web服务负责管理事务和所有RavenDB交互。大多数'Command-ish'服务操作包括:a)加载由请求标识的域对象(文档),b)调用业务逻辑,c)将结果转换为响应DTO。我们已经扩充了ServiceStack,以便许多Command-ish操作使用自动处理程序来执行上述所有操作,而无需任何代码。 'Query-ish'服务操作通常包括:a)对RavenDB执行查询,b)将查询结果转换为响应DTO(实际上这通常作为a的一部分完成),在查询处理期间使用RavenDB /指数/变压器)。业务逻辑始终被推送到域层。

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

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

  5. 至于其他客户,我们有两种选择。我们可以引用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);
        }
    }
    

答案 1 :(得分:2)

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

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

这取决于当然的情况,但简单地说,如果你用某种方式用SQL Server构建你的应用程序,那么对RavenDB做同样的事情。如果您有DAL层,ORM,存储库模式或具有SQL Server后端的任何内容,请对RavenDB执行相同操作。如果您不介意泄漏抽象,或者项目规模小到不能保证抽象您的数据访问,请相应地进行编码。

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

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