业务逻辑层的设计

业务逻辑层的设计

你或许也和我一样:

一谈到业务逻辑层,脑海中定会呈现三个字母,那就是“BLL”,我曾经写过的第一个类就非常简单,里面几乎什么也没有,后来就发现它就是个坑,为了填补这个坑,来后一个项目我根本就没有打算要分这层给它了。

直到有一天,辗转反侧睡不着,半夜起来敲代码,我终于领悟了这个BLL的真谛。

我还是用以前的话,“软件设计本身已经够理性了,我们为什么不能用感性一点的文字?”,写者随意,看者轻松。

声明:我会讲一些术语讲的比较白,但可能缺少严谨,但是有些词语不明白可能还是需要搜索一下了。

哪些逻辑应该划分到数据访问层(DAL)

有很长一段时间,我的业务逻辑代码遍布在整个项目代码的任何角落,也同时在操作ADO.NET调用代码的中见缝插针,但我相信这是从一个程序员走向设计师必定要踩过的坑。

之后开始使用O/RM工具,是的,用了它之后我很少使用ADO.NET,因为都被它分装好了,当然也就很少有机会见缝插针了。

在O/RM以及领域驱动设计的思想引领下,我也逐渐在避免存储过程,同时这个习惯也让公司许多业务逻辑从存储过程中移除。

但是O/RM工具在执行事务的时候有两种方式,原理如下:

1、大概是将要增删改的东西全部丢到一个类似于IList<T>的集合之后,当你调用提交事务时,其实是遍历这个IList<T>的集合一次性操作数据库,他又成功地将业务逻辑与操作数据库分离,当你要它回滚的时候,它只需要将集合清空即可。专业点的名词讲,这个叫工作单元(uow)。

2、有时候你会发现,新增了一个实体(或者insert)了以后,你需要马上用到它的ID(标识)继续执行一些相关的操作,这些操作都包含在一个事务中。而且经常在事务中也会穿插那么一点点的逻辑,但通常这些逻辑都比较细粒度,或者说应该将它设计成模块化(讲白点,就是设计时考虑内聚性)。

例如,有个简单的需求是这样的。你需要新增一张订单,订单中包含几个项,所以你在新增订单的同时连同它包含的所有项都保存进数据库。

Order、OrderItem显然是对象模型,存在于BLL。

复制代码
    public class Order
    {
        private IList<OrderItem> _orderItems = new List<OrderItem>();

        public virtual IList<OrderItem> OrderItems
        {
            get { return _orderItems; }
            set { _orderItems = value; }
        }

        public virtual string Name { get; set; }
    }

    public class OrderItem
    {
        public virtual string ItemName { get; set; }
    }
复制代码


你将会发现,这个类中仅包含操作数据库的逻辑。验证、业务规则都不是它要关注的。在领域驱动设计中,会称Repository为仓储。OrderRepository就是一个特定的仓储,它不需要知道订单中具体有多少个订单项,也不关心它们的名称是否符合规范,这些都应该是BLL的责任,而仓储属于DAL。

复制代码
    public class OrderRepository
    {
        public void AddOrder(Order order)
        {
            //新增一个Order,并持久化
            foreach (OrderItem item in order.OrderItems)
            {
                //遍历所有项,添加到这个订单。
            }
        }
    }
复制代码


当我平时在设计的时候,耳边老是有DAL的一个声音,“嘿,这不是我的责任,不要给我做。”

DAL开始踢皮球了。听久了,我逐渐听到了DAL的心声,我开始将许多IF交给BLL来做。

那么BLL应该做些什么?

上面已经提到了验证和业务规则。

让对象模型生动起来,让BLL充实起来,修改Order类

复制代码
    public class Ordere
    {
        private string _name;
        private IList<OrderItem> _orderItems = new List<OrderItem>();

        public virtual IList<OrderItem> OrderItems
        {
            get { return _orderItems; }
            set { _orderItems = value; }
        }

        public virtual string Name
        {
            get { return _name; }
            set
            {
                if (value.Length <= 0 && value.Length > 25)
                {
                    throw new IndexOutOfRangeException("订单名称必须在0-25个字符以内");
                }
                _name = value;
            }
        }

        public bool IsValid
        {
            get
            {
                if (Name.Length <= 0 || Name.Length > 25)
                {
                    return false;
                }
                return true;
            }
        }

        public string Vali()
        {
            StringBuilder builder = new StringBuilder();
            if (!this.IsValid)
            {
                if (Name.Length <= 0 || Name.Length > 25)
                {
                    builder.AppendLine("订单名称必须在0-25个字符以内");
                }
            }
            return builder.ToString();
        }
    }
复制代码

下面来模拟一下实际调用验证

复制代码
        [Test]
        public void TestVali()
        {
            Ordere order = new Ordere();
            order.Name = "";
            if (!order.IsValid)
            {
                //Name长度为0显然不合法,这里果断没有通过验证。
                Console.WriteLine(order.Vali());
                //Vali方法让我们知道了验证没有通过的原因
                //订单名称必须在0-25个字符以内
                //Expected: True
                //But was:  False
            }
            Assert.AreEqual(false, order.IsValid);
        }
复制代码

实际使用当中,其实完全可以引入微软企业库5.0的验证模块,并且发现IsValid和Vali()都可以在该框架的帮助下完美提取成一个方面(AOP切面编程)。

实际使用当中,也会有一个服务类来服务于客户端的调用,在这里验证,也在这里调用仓储。


小结:由于时间关系,我只能暂时写到这里了,看来写技术博客真的很累,但是写的结果就是对自己和读者都有帮助。

需要注意的是,这篇随笔提到的设计思路,有点偏领域驱动设计的。

其实业务逻辑层也是非常有学问的,这篇随笔也只能点到一个方面,就是哪些逻辑不应该把责任推给数据访问层,也提到了一些处理业务规则和验证的经验。

 
 
分类: 架构设计
原文地址:https://www.cnblogs.com/Leo_wl/p/3193198.html