95992828九五至尊2

实例之旅,让大家从目的早先吧

二月 7th, 2019  |  882828九五至尊手机版

   
听说DDD-“领域驱动开发”已经很久了,园子里面已经有诸多大牛写过博文介绍,但自己一贯未曾尝试过,直到二零一九年供销社的一个品类出现数据库移植,原来的事务逻辑都写在SqlServer的囤积进度中,现在要移植到PostgreSQL中,才真切的认知到,再持续走“表驱动开发”的形式,没有好前途了。于是,花了多少个礼拜,来推行一下领域驱动开发那种支付格局。

 前些天咱们经过一个“超市收银”业务来作为大家的演示(就算那些示例看上去不太健康,不过它确表述大家所要求的)。大家将从作业分析到业务建模然后最后的编码来用“面向世界对象”的法子来做大家的品种。

    
征得《世界对象驱动开发:来吧,让大家从目的开端吧》原文小编的允许,我拔取文中的“超市收银”业务场景,开发了一个“超市管理体系”–PDF.NET
Super马克et
MIS,此处是下载地址。本连串将会讲课这么些开发进程的关于技术细节,但作为那几个连串的开赛,仍旧先说说世界驱动开发那一个开发情势给自己的不一样等感觉到的地点。

好,大家初叶吧!

 

 

天地驱动开发情势

一、业务分析

     一、分析工作必要。

   
超市管理种类包含收银管理,商品管理,设备管理,雇员管理,客户保管等几有些,其中收银管理包括收银员管理,收银机管理,收银台管理;商品管理包涵商品为主音信保管,商品存货音信保管;设备管理、雇员管理和客户保管都是帮扶的,相比简单,系统的为主仍然“收银进程”,注意是“进程”而不是“管理”,说到管理很不难落入“管理体系”的笔触,说“进程”更便于跟工作场景,业务用例,业务流程等组合起来。

 

    收银业务场景:

   
顾客购买商品之后,来到收银台,收银员检查扫描商品,收银机展现商品的价钱清单,收银员通告客户货物总价格,客户确认,付款,完结收银。

世家都去超市买过东西,对超市收银业务都相比较熟稔。什么?你不熟?行吗,那大家找个收银员给我们讲解下(领域专家)。

   收银业务用例:

  
这里不严谨分裂“场景”和“用例”,我认为“用例”是更进一步技术化的词汇。在该用例中,有一些角色,如顾客,收银员,收银机,还有部分目的,如商品,购物车,价格单,有局地移动,如扫描商品(读条码),统计价格,付款。

 

   收银业务流程:

  
大家都去过超市,流程简便来说很简短,但规范来说依然很复杂,那里自己不自作聪明了。

 

收银员小慧:哦,是这么呢。顾客排队银帐我就收银呢,我要采取收银机呢。收银机就能猜想出要收的钱啊,我就扫一下吗,就OK了吗。然后就收银了呢。

    二、设计领域对象模型

   
在《天地对象驱动开发:来吧,让大家从目的开首吧》一文中,作者曾经付诸了世界对象模型,那里也不在重复,但是自己安排的模型与原小编有点细微差异,那么些将来再说。

 882828九五至尊手机版 1

   
有了DomainModel,在系统进入完美开发以前,就可以测试DomainModel,从而证实系统的主干逻辑设计是还是不是站得住。

 

    三、测试领域对象模型

   
为何要这一步?因为大家透过前面的事体分析之后,得到了大家的园地对象模型,但大家的接头是还是不是科学吧?为了印证大家的敞亮是还是不是正确,须要对第二步中的模型举办测试,看它是或不是科学,是或不是站得住。

 

 static void TestModel()
        {
            //http://www.cnblogs.com/assion/archive/2011/05/13/2045253.html

            //大家创造几样货物
            GoodsStock RedWine = new GoodsStock() { GoodsName = “红酒”, GoodsPrice = 1800, GoodsNumber = 10 };
            GoodsStock Condoms = new GoodsStock() { GoodsName = “安全套”, GoodsPrice = 35, GoodsNumber = 10 };

            //大家创设几位消费者
            Customer Chunge = new Customer() { CustomerName = “春哥” };
            Customer Beianqi = new Customer() { CustomerName = “贝安琪” };
            Customer Noname = new Customer();

            //有一台收银机
            CashierRegisterMachines crManchines = new CashierRegisterMachines() { CashRegisterNo = “CR00011” };
            //当然,大家要求收银员啊
            Cashier CashierMM = new Cashier(crManchines) { CashierName = “收银员MM”, WorkNumber = “SYY10011” };

           

            //顾客开首排队结帐了
            Queue<Customer> customerQueue = new Queue<Customer>();
            customerQueue.Enqueue(Chunge);
            customerQueue.Enqueue(Beianqi);
            customerQueue.Enqueue(Noname);

            //队伍容貌过来,按先后顺序挨个收银喽
            foreach (var customer in customerQueue)
            {
                //收银
                CashierMM.CashRegister(customer);
            }

        }

 

听了小慧的上书,大家心里有了政工的概念了。大家那里运用《业务主要字分析法》来找出此业务流程里面的一部分关乎字:

    四、设计工作处理类

   
大家在第三步编写领域对象模型的测试案例的时候,实际上就是针对工作场景的测试,这几个处负责人情场景的代码大致就是“业务处理类”–BIZ
class,大家把它提取出来,在得心应手下,就获得实在的事务处理类了。这么些事情处理类后续还会直接无微不至的,但那边曾经基本成型了。

 882828九五至尊手机版 2

 

    五、设计Entity和ViewModel

   
在一帆风顺工作处理类的时候,大家需求分析哪些领域对象的特性须求持久化,注意不要单个的去分析世界对象,而要根据满世界对象模型去分析,比如可能有多少个领域对象会利用一个持久化属性的,这么些时候我们理应考虑将这些特性放到一个实体对象中,那样我们就取得了系统要求的实体类(Entity);分析哪些领域对象的性质可能是索要给用户界面(View)使用的,同样的原因,可能会结合多少个世界对象的属性给一个用户界面,那样我们就拿走了ViewModel。

    说简练点,Entity 和ViewModel 都依靠于 BIZ class ,BIZ
class调度DomainModel,使用或爆发Entity和ViewModel。

   
BIZ似乎工成效例,它整合MomainModel的调用,我那边这些Biz也许更像Service。系统唯有Entity会和数据库打交到。

 882828九五至尊手机版 3

Entity

 

882828九五至尊手机版 4

ViewModel

商品

      六、测试工作处理类

   
大家早已在第三步中测试了世界对象模型,当时的多寡都是模仿的,没有使用数据库,现在我们编辑一些测试案例来进展真正的测试了。测试案例可以利用VS自带的单元测试来做,也足以编制专门的测试项目,或者直接编写不难的测试页面。由于“领域对象模型”已经测试过,所以这一步的测试我们的事体操作类是或不是可以正确的军事管制世界对象,能够生成ViewModel等。

 

static void TestBIZ()
        {
            //我们创设几样商品
            GoodsStock RedWine = new GoodsStock() { GoodsName = “红酒”, GoodsPrice = 1800, GoodsNumber = 10, SerialNumber =”J000111″ };
            GoodsStock Condoms = new GoodsStock() { GoodsName = “安全套”, GoodsPrice = 35, GoodsNumber = 10, SerialNumber =”T213000″ };

            //大家创造几位消费者
            Customer Chunge = new Customer() { CustomerName = “春哥” };
            Customer Beianqi = new Customer() { CustomerName = “贝安琪” };
            Customer Noname = new Customer();

            //有一台收银机
            CashierRegisterMachines crManchines = new CashierRegisterMachines() { CashRegisterNo = “CR00011” };
            //当然,大家须要收银员啊
            Cashier CashierMM = new Cashier(crManchines) { CashierName = “收银员MM”, WorkNumber = “SYY10011” };

            //顾客逛了一圈,选了和谐想要的商品
            Chunge.LikeBuy(RedWine.TakeOut(1));
            Beianqi.LikeBuy(RedWine.TakeOut(1));
            Beianqi.LikeBuy(Condoms.TakeOut(1));
            Noname.LikeBuy(Condoms.TakeOut(2));

            //调用收银业务类
            CashierRegisterBIZ biz = new CashierRegisterBIZ(CashierMM ,crManchines);
            biz.AddQueue(Chunge);
            biz.AddQueue(Beianqi);
            biz.AddQueue(Noname);

            biz.CashierRegister();

        }

 

顾客

    七、设计表架构 

    有了Entity对象,很自然的就足以获取一定数据库系统的创造表的脚本了。超市管理连串接纳了PDF.NET框架的实体类,实体类的习性和表的字段映射关系非凡精通,由此可以一贯从实体类得到创立表的台本。运行种类的建表脚本,那样大家的数据库就建好了,系统现已得以运作了。

收银员

 882828九五至尊手机版 5

收银机

 

*收银

 八、开发用户界面

   
终于等到这一步了,好多时候自己都想直接跨过前边7个步骤,先做这一步的,但为了举行“领域驱动开发”方式,仍然坚持不渝了下来。系统应用ASPX页面作为用户界面,在这一步中,依据要呈现的功效,设计对应的页面,调用BIZ
class得到ViewModel,将它绑定到页面上。就算说M(BIZ
class),V(ASPX页面),VM(ViewModel),那种格局是否很像神话中的MVVM呢?

 

上边是系统的有关用户界面:

882828九五至尊手机版 6

会员登录页

 

882828九五至尊手机版 7

消费者购物首页

 

*选商品

表驱动开发方式

   
这是本人从前以及大家商家现有项目,还有为数不少集团做项目的支付方式,详细说说大家集团近来一个类其余支出进度吧:

*收银员使用收银机

    一、分析要求,制作静态页面作为Demo

   
须要人员守着美工制作人士,一个个模块,一个个效益的成立好所有的静态页面,作为系统的Demo。这些进度很长,须求人士时时会频仍的改动须求,然后让美工修改界面,提醒这么些界面反应的连串机能。以下简称这一个静态页面为Demo页面。

*收银机扫商品计量金额

    二、从Demo页面了解系统机能

   
开发人士、必要人员、美工制作人员一起来看那么些Demo页面,开发人士提问,需要人士解答,若是开发人士提出质询和不客观之处,再由图画去修改。当然,效率上开发人士是无权否定和改动的,只是第功效的布局和突显格局提议意见,方便程序开发落成。那些进度开支的时间也很长,经常一个个页面的过,而且开发人士事先已经有了分工,每个人承担一个模块,听到自己负责的模块的时候,就打起精神来听,蒙受跟自己不相干的模块,也就是名不副实而已,在会议室耗时间。

 

    三、举办表设计

   
这一个历程有DBA主导,每个模块的官员和DBA一道,依据Demo页面上面显示的作用、表单、表单域,来统筹这么些模块相关的表和表的字段。这一个进程也要消耗较长的年华,首要纠结于该用什么英文名称来对应表字段,和太多的表以及表字段的含义、类型。

好了,列出那一个“业务紧要字”了,大家就可以建大家的目的模型了。

    四、开发页面,设计存储进度

   
开发人士根据早期的Demo页面,用ASP.NET来落实一回,首要的劳作就是写好多JS代码,来动态调用后台数据。而DBA就将数据工作全包了,为开发人士编写一个个的SQL函数,输出每个页面用到数码,为了接通方便,输出的多少字段名称用的是汉语;程序的效应,也就是所谓的事体逻辑,就由DBA全部写在储存进程中了。DBA专心写存储进度,使用SqlServer
2008上的那一个最酷的特点;开发人士专心做ASPX页面,BLL层只是一个尾巴,DAL层已经由PDF.NET的代码生成器自动生成了,不用开发人士操心,只是问问DBA那些功能的SQL该怎么写而已。DBA乐得专心,开发人士乐得简单(即使是体力活),那样咱们都HAPPY

 

    五、测试

   
等开发人士开发万有着的模块,DBA写完了颇具的贮存进度,测试人士终于上阵了。一阵测试下来,Bug不少,重如果多少错乱,成效完毕跟需要有差异,我们又惊慌的始发改Bug了。大家的可怜项目支付用了2个月,而测试改Bug花了4个多月啊,有些同事受不住测试人士和需要人士的“轮番攻击”只能离开了,大家都盼望着快点为止那一个类型,太累了。

 

   
项目费用形成了,有人问我们的系统有无业模型,大多数都不置可以依旧不可以,说到“去看数据库吧”;问开发技术有没有增高,答案是对JS更谙习了,其余的就从未有过了;再问你们的开支文档怎么着,回道“开发文档早就没用了”,有标题直接去看代码吧。

882828九五至尊手机版,    一个品种一个档次就是那般开发的,三年五载,日复一日,我们的工作就是写代码,改Bug,没有啥变动。

 

二、系统建模

 二种开发形式的分歧

    上面,回过头来看看“领域驱动开发”格局,有哪些不雷同的地点:

  1.    
    领域驱动器重“领域对象模型”的布置性,可以先规划,再测试,最终才开发;
  2.     领域驱动可以爆发系统的着力价值–“领域对象模型”;
  3.    
    领域驱动使得所有开发进度更便于关心系统的紧要成效,使得“见兔放鹰”;
  4.     领域驱动无需重点关切数据难题,使得系统跨数据库移植万分简单;
  5.     领域驱动更关心“业务”,而不是“数据本身”,适合业务格外复杂的情形;
  6.    
    领域驱动更关切“业务对象”,从而可以运用各种设计方式,架构格局,使得系统更便于扩大和优化。

 关于那点,在大家现有系统中深有体会,由于有着业务逻辑的写到了蕴藏进度中,而如今系统运转功能相比较低下,在不更改硬件的前提下,想优化的长空都尚未。

 

    当然,表驱动开发情势出现百无一用,它比较适的情状是:

  1.     付出集团的完好统筹力量欠缺;
  2.     项目标事体不是很复杂,不平时改变业务效能;
  3.     以数量为主干,数据在项目中有着焦点价值;
  4.     有很强的DBA团队

 

   平日很多种类工作也相比较复杂,也不是以数据为骨干,也尚未很强的DBA团队,但依旧接纳“表驱动开发情势”,我想根本原因应该是“ 开发社团的完整规划力量欠缺”,而项目或产品“设计的三六九等”是一直影响项目或制品的“花费”,甚至是“成败”的。“设计应对转移”,看您是还是不是认同了。

 

   
原来的表驱动开发格局,只会傻傻的根据页面的DEMO,得知应该有哪些表和字段,很难分析出中间的纷纭工作对象和相关联的业务流程,做出来的顺序每个部分都是惨重“割裂”的!
   
领域驱动开发格局,是先分析需要,得到世界模型,然后和事务一起验证该模型,逐步改进宏观模型,第二步是促成工作场景,获得什么样领域对象的性质是索要持久化的,得到怎么样组合的习性是亟需给前端突显的(ViewModel),第三步才是设计View,使用ViewModel,设计实体类,最后才起来开发用户界面。

   
作为那个连串的开业,先和豪门商量一下世界驱动开发形式与传统表驱动开发方式的不平等之处,那里写的是我的某些清醒,由于是理论性质的,所以将“超市管理连串”的实例放到下篇讲解。

 

 

地点大家解析出了一部分“业务首要字”接下去大家分析这个事情根本字并长远他们的工作。

 

商品对象(Goods)。

属性:商品名称(GoodsName)、商品价位(GoodsPrice)。

行事:在此地商品对象是绝非表现的,大家也足以叫它“值对象”。

 

买主对象(Customer)。

特性:顾客姓名(CustomerName)、顾客购买的货物(Goodss)

行事:选购想买的货品(LikeBuy)、听收银员说要收多少RMB(ListenAmount)

 

收银员对象(Cashier)。

品质:收银员姓名(CashierName)

行为:收银(CashierRegister)

 

收银机对象(CashierRegister)。

特性:收银机编号(CashRegisterNo)

字段:总金额(_totalAmount)

作为:收银(CashRegisters)、突显收银总额(ShowAmount)

 

882828九五至尊手机版 8

 

有木有!有木有?有木有很直观,那也就是面向对象分析的补益,因为对象就是对实际的抽象,大家现实中的事务能够很有益的用对象抽象出来。大家很简单发觉,那和用表来叙述那几个工作模型鲜明要不便于的多。表还只可以描述属性,造成了质量与表现的诀别。

 

三、代码示例

 

商品对象

 

/// <summary>
    /// 商品
    /// </summary>
    public class Goods
    {
        /// <summary>
        /// 对象标识
        /// </summary>
        public Guid OKey { get; set; }
        /// <summary>
        /// 商品名称
        /// </summary>
        public string GoodsName { get; set; }
        /// <summary>
        /// 商品价位
        /// </summary>
        public decimal GoodsPrice { get; set; }
    }

 

消费者对象

 

/// <summary>
    /// 顾客
    /// </summary>
    public class Customer
    {
        /// <summary>
        /// 对象标识
        /// </summary>
        public Guid OKey { get; set; }
        /// <summary>
        /// 顾客姓名
        /// </summary>
        public string CustomerName { get; set; }

        private List<Goods> _goodss = new List<Goods>();
        /// <summary>
        /// 顾客选购的货品
        /// </summary>
        public List<Goods> Goodss
        {
            get { return _goodss; }
            set { _goodss = value; }
        }

        /// <summary>
        /// 顾客选购商品
        /// </summary>
        /// <param name=”goods”>商品</param>
        public void LikeBuy(Goods goods)
        {
            this._goodss.Add(goods);
        }

        /// <summary>
        /// 听收银员应收多少钱
        /// </summary>
        /// <param name=”amount”></param>
        public void ListenAmount(decimal amount)
        {
            Console.WriteLine(“我是[{0}],我买了{1}件货物。我共花了{2}元RMB。”, this.CustomerName, this.Goodss.Count, amount.ToString(“f2”));
        }

 

收银员对象

 

/// <summary>
    /// 收银员
    /// </summary>
    public class Cashier
    {
        /// <summary>
        /// 对象标识
        /// </summary>
        public Guid OKey { get; set; }
        /// <summary>
        /// 收银员姓名
        /// </summary>
        public string CashierName { get; set; }

        /// <summary>
        /// 收银
        /// </summary>
        /// <param name=”customer”>顾客</param>
        public void CashRegister(Customer customer)
        {
            //打开使用收银机
            CashRegister cashRegister = new CashRegister();

            //对顾客的货物进行收银机扫码,收银
            foreach (var goods in customer.Goodss)
            {
                //使用收银机扫商品举行收银
                cashRegister.CashRegisters(goods);
            }

            //布告顾客一共收多少钱
            customer.ListenAmount(cashRegister.ShowAmount());
        }
    }

 

收银机对象

 

/// <summary>
        /// 对象标识
        /// </summary>
        public Guid OKey { get; set; }
        /// <summary>
        /// 收银机编号
        /// </summary>
        public string CashRegisterNo { get; set; }

        /// <summary>
        /// 总价格
        /// </summary>
        private decimal _totalAmount { get; set; }

        public CashRegister()
        {
            //收银总额置0
            this._totalAmount = 0;
        }

        /// <summary>
        /// 收银
        /// </summary>
        /// <param name=”goods”>商品</param>
        public void CashRegisters(Goods goods)
        {
            this._totalAmount += goods.GoodsPrice;
        }

        /// <summary>
        /// 呈现收银总额
        /// </summary>
        /// <returns></returns>
        public decimal ShowAmount()
        {
            return this._totalAmount;
        }

 

效仿业务流程

 

//大家成立几样货物
            Goods RedWine = new Goods() { GoodsName = “红酒”, GoodsPrice = 1800,OKey=Guid.NewGuid() };
            Goods Condoms = new Goods() { GoodsName = “安全套”, GoodsPrice = 35,OKey=Guid.NewGuid() };

            //我们创设几位顾客
            Customer Chunge = new Customer() { CustomerName = “春哥”, OKey = Guid.NewGuid() };
            Customer Beianqi = new Customer() { CustomerName = “贝安琪”, OKey = Guid.NewGuid() };

            //当然,我们须要收银员啊
            Cashier CashierMM = new Cashier() { CashierName = “收银员MM”, OKey = Guid.NewGuid() };

            //顾客逛了一圈,选了温馨想要的货品
            Chunge.LikeBuy(RedWine);
            Beianqi.LikeBuy(RedWine);
            Beianqi.LikeBuy(Condoms);

            //顾客早先排队结帐了
            Queue<Customer> customerQueue = new Queue<Customer>();
            customerQueue.Enqueue(Chunge);
            customerQueue.Enqueue(Beianqi);

            //队伍容貌过来,按先后顺序挨个收银喽
            foreach (var customer in customerQueue)
            {
                //收银
                CashierMM.CashRegister(customer);
            }

 

显示结果

 

882828九五至尊手机版 9

 

上边的例证即便不是很适宜,但是它也很好的像大家表明出了世界驱动分析难题、面向对象驱动开发的功利了。

 

最终咱们回看一下,用数据库表驱动的艺术。分析这些工作会是何等样子的。。。。

Your Comments

近期评论

    功能


    网站地图xml地图