• 业务逻辑层的设计


    业务逻辑层的设计

    你或许也和我一样:

    一谈到业务逻辑层,脑海中定会呈现三个字母,那就是“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切面编程)。

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


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

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

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

     
     
    分类: 架构设计
  • 相关阅读:
    Flask 随记
    Notes on Sublime and Cmder
    Algorithms: Design and Analysis Note
    LeetCode 215 : Kth Largest Element in an Array
    LeetCode 229 : Majority Element II
    LeetCode 169 : Majority Element
    LeetCode 2:Add Two Numbers
    LeetCode 1:Two Sum
    Process and Kernel
    安装好scala后出现“找不到或无法加载主类”的问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3193198.html
Copyright © 2020-2023  润新知