• Domain Model模式


    设计模式学习日记三(持续更新)

    本篇介绍Domain Model模式,一个银行领域建模,涉及帐号的创建以及帐号之间的现金转账的小程序。

    1. POCO 和 PI

    前面提到过,Domain Model 与 Active Record 模式不同,它不知道持久化。术语持久化(persistence ignorance,PI)表示普通CLR对象(plain old common runtime object,POCO)业务实体的朴实本质。通常Repository模式(后续中将会介绍该模式)将 Domain Model 的业务对象持久化。

    2. 示例程序

    创建名为Terminator.Practice.DomainModel的解决方案,并添加下面的类库项目:

    Terminator.Practice.DomainModel.Model -- 包含应用程序内所有的业务逻辑。领域对象将存放在此处,并与其他对象建立关系,从而表示应用程序正则构建的银行领域。该项目还将以接口的形式为领域对象持久化和检索定义契约,采用Repository模式来实现所有的持久化管理需求。Model项目不会引用其他任何项目,从而确保:让它与任何基础设施关注点保持隔离,只关注业务领域。

    Terminator.Practice.DomainModel.AppService -- 充当应用程序的接口(API)。表示层将通过消息(简单的数据传输对象,后续中将会介绍消息传送模式)与 AppService 通信。AppService 还将定义视图模型,这些是领域模型的展开视图,只用于数据显示(后续中将会更详细的介绍该方式)。

    Terminator.Practice.DomainModel.Repository -- 包含Model项目中定义的资源库接口的具体实现。Repository引用了Model项目,从而从数据库提取并持久化领域对象,Repository项目只关注领域对象持久化和检索的责任。

    添加一个新的Web应用程序 Terminator.Practice.DomainModel.UI.Web -- Web项目负责应用程序的表示和用户体验需求,这个项目只与

    AppService交互,并接收专门为用户体验视图创建的强类型视图模型。

    对Repository项目添加对Model项目的引用,AppService项目添加对Model和Repository项目的引用,Web项目添加对AppService项目的引用。

    创建名为BankAccount.mdf的数据库,创建两张表:

    BankAccounts表
    列名 数据类型 是否允许空
    BankAccountId uniqueidentifier,Primary key False
    Balance money False
    CustomerRef nvarchar(50) False
    Transactions表
    列名 数据类型 是否允许空
    BankAccountId uniqueidentifier False
    Deposit money False
    Withdrawal money False
    Reference nvarchar(50) False
    Date datetime False

    现在开始领域建模,在这个场景中,BankAccount为发生的每个动作创建一个Transaction对象,类似于操作日志的功能。

    在Model项目中添加一个新类Transaction:

    Transaction类代码

    注:Transaction对象被称为值对象,这是DDD(领域驱动设计)中的一个术语,后续中将会介绍DDD。

    添加第二个类 BankAccount:

    BankAccount类代码

    BankAccount类有3个简单的方法:CanWithdraw,Withdraw,Deposit。因为有一个CanWithdraw方法,所以应该期望调用代码在尝试从某个帐号取出现金时使用Test-Doer(先测试再执行)模式:

    if(myBankAccount.CanWithdraw(amount))
    {
        myBankAccount.Withdraw(amount);
    }

    如果在不进行检查的情况下对一个没有足够现金金额的帐号使用Withdraw方法,则应该抛出一个异常,为此,定义一个异常类,向Model项目添加InsufficientFundsException类:

    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.Model
    {
        public class InsufficientFundsException : ApplicationException 
        {
        }
    }
    复制代码

    现在需要某种方法来持久化BankAccount和Transaction,因为不希望污染Domain Model项目,所以添加Repository的接口来定义契约,以满足实体的持久化和检索需求,这就是前面提到的PI和POCO的概念。

    创建一个带有如下契约的接口IBankAccountRepository:

    复制代码
    using System;
    using System.Collections.Generic;
    
    namespace Terminator.Practice.DomainModel.Model
    {
        public interface IBankAccountRepository
        {
            void Add(BankAccount bankAccount);
            void Save(BankAccount bankAccount);
            IEnumerable<BankAccount> FindAll();
            BankAccount FindBy(Guid AccountId);
        }
    }
    复制代码

    向Model项目中添加BankAccountService类:

    BankAccountService类代码

    在BankAccountService 的当前实现中,在保存两个银行帐号之间发生的任何错误均会让数据处于非法状态,因此我们需要一个事务保护机制(本篇没有这么做),后续中将会介绍UoW(工作单元模式),它能够确保所有的事务作为一个原子动作进行提交,万一出现异常就回滚。

    现在可以编写方法来持久化BankAccount和Transaction对象,在Repository项目添加一个BankAccountRepository类,该类将实现接口IBankAccountRepository:

    BankAccountRepository类代码

    由于Repository项目需要访问web.config,所以需要添加对System.Configuration的引用。

    注:在后续中将介绍一些常见的对象关系映射器,能够节省编写ADO.NET基础设施代码的时间

    现在可以为客户端添加一个服务层,让其与系统已一种简单的方式进行交互。

    在AppService项目中添加一个新文件夹 ViewModel,并向其中添加两个类BankAccountView 和 TransactionView:

    复制代码
    using System;
    using System.Collections.Generic;
    
    namespace Terminator.Practice.DomainModel.AppService.ViewModels
    {
        public class BankAccountView
        {
            public Guid AccountNo { get; set; }
            public string Balance { get; set; }
            public string CustomerRef { get; set; }
            public IList<TransactionView> Transactions { get; set; } 
        }
    }
    复制代码
    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.AppService.ViewModels
    {
        public class TransactionView
        {
            public string Deposit { get; set; }
            public string Withdrawal { get; set; }
            public string Reference { get; set; }
            public DateTime Date { get; set; }
        }
    }
    复制代码

    BankAccountView 和 TransactionView 提供了领域模型用于表示的展开视图,为了将领域实体转换成数据传输视图模型,需要映射器类(后续中将会介绍一种将这个过程自动化的方法)。

    在AppService项目根目录创建一个新类ViewMapper:

    复制代码
    using System.Collections.Generic;
    using Terminator.Practice.DomainModel.AppService.ViewModels;
    using Terminator.Practice.DomainModel.Model;
    
    namespace Terminator.Practice.DomainModel.AppService
    {
        public static class ViewMapper
        {
            public static TransactionView CreateTransactionViewFrom(Transaction tran)
            {
                return new TransactionView
                {
                    Deposit = tran.Deposit.ToString("C"),
                    Withdrawal = tran.Withdrawal.ToString("C"),
                    Reference = tran.Reference,
                    Date = tran.Date
                };
            }
    
            public static BankAccountView CreateBankAccountViewFrom(BankAccount acc)
            {
                return new BankAccountView
                {
                    AccountNo = acc.AccountNo,
                    Balance = acc.Balance.ToString("C"),
                    CustomerRef = acc.CustomerRef,
                    Transactions = new List<TransactionView>()
                };
            }
        }
    }
    复制代码

    向AppService项目中再添加一个文件夹Messages,该文件夹将包含所有用来与服务层通信的Request-Reponse模式对象,因为所有的Reponse都共享一组相同的属性,所以可以创建一个基类ResponseBase:

    复制代码
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public abstract class ResponseBase
        {
            public bool Success { get; set; }
            public string Message { get; set; }
        }
    }
    复制代码

    Success属性表示被调用的方法是否成功运行,而Message属性包含该方法运行结果的详细信息。

    在Messages文件夹添加几个类:BankAccountCreateRequest、BankAccountCreateResponse、DepositRequest、FindAllBankAccountResponse、FindBankAccountResponse、TransferRequest、TransferResponse、WithdrawalRequest

    复制代码
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class BankAccountCreateRequest 
        {        
            public string CustomerName { get; set; }
        }
    }
    复制代码
    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class BankAccountCreateResponse : ResponseBase 
        {        
            public Guid BankAccountId { get; set; }
        }
    }
    复制代码
    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class DepositRequest
        {
            public Guid AccountId { get; set; }
            public decimal Amount { get; set; }
        }
    }
    复制代码
    复制代码
    using System.Collections.Generic;
    using Terminator.Practice.DomainModel.AppService.ViewModels;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class FindAllBankAccountResponse : ResponseBase 
        {
            public IList<BankAccountView> BankAccountView { get; set; }
        }
    }
    复制代码
    复制代码
    using Terminator.Practice.DomainModel.AppService.ViewModels;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class FindBankAccountResponse : ResponseBase 
        {
            public BankAccountView BankAccount { get; set; }
        }
    }
    复制代码
    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class TransferRequest
        {
            public Guid AccountIdTo { get; set; }
            public Guid AccountIdFrom { get; set; }
            public decimal Amount { get; set; }
        }
    }
    复制代码
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class TransferResponse : ResponseBase
        {        
        }
    }
    复制代码
    using System;
    
    namespace Terminator.Practice.DomainModel.AppService.Messages
    {
        public class WithdrawalRequest
        {
            public Guid AccountId { get; set; }
            public decimal Amount { get; set; }
        }
    }
    复制代码

    消息传送对象就位之后,就可以添加服务类来协调对领域实体(服务和资源库)的方法调用,在AppService项目的根目录添加一个新类ApplicationBankAccountService:

    ApplicationBankAccountService类代码

    ApplicationBankAccountService类协调应用程序活动并将所有的业务任务委托给领域模型,该层并不包含任何业务逻辑,有助于防止任何与业务无关的代码污染领域模型项目,该层还将领域实体转换成数据传输对象,从而保护领域的内部操作,并为一起工作的表示层提供了一个易于使用的API。

    为了简单起见,这里选择了简陋的依赖注入方式并对默认的构造器硬编码,以便使用已经编码的资源库领域服务实现,后续中将介绍IoC和IoC容器来提供类的依赖关系。

    最后就是创建用户界面,从而能够创建帐号并执行交易。

    Default.aspx页面
    Default.aspx.cs代码
    Default.aspx.designer.cs代码

     web.config数据库连接字符串配置

      <connectionStrings>
        <add name="BankAccountConnectionString"
                 connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\BankAccount.mdf;Integrated Security=True;User Instance=True"
                 providerName="System.Data.SqlClient"/>
      </connectionStrings>

    解决方案结构图

    程序运行图:

    最后附上demo源码

     
     
    标签: GoFSOLIDDomain Model依赖注入

    Design Pattern 设计模式

    设计模式学习日记三(持续更新)
    摘要: 本篇介绍Domain Model模式,一个银行领域建模,涉及帐号的创建以及帐号之间的现金转账的小程序。1. POCO 和 PI前面提到过,Domain Model 与 Active Record 模式不同,它不知道持久化。术语持久化(persistence ignorance,PI)表示普通CLR对象(plain old common runtime object,POCO)业务实体的朴实本质。通常Repository模式(后续中将会介绍该模式)将 Domain Model 的业务对象持久化。2. 示例程序创建名为Terminator.Practice.DomainModel的解决方案,并添加阅读全文

    posted @ 2013-01-06 08:59 丶Terminator 阅读(337) | 评论 (4) 编辑

    设计模式学习日记二(持续更新)
    摘要: 上一篇主要介绍了一些设计原则和模式,本章来详细介绍一下Active Record模式,一个Blog小程序。导航到http://sourceforge.net/projects/castleproject/files/ActiveRecord/3.0/Castle.ActiveRecord-3.0.RC.zip/download下载ActiveRecord项目的最新版。创建名为Terminator.Practice.ActiveRecord 的解决方案,添加名为Terminator.Practice.ActiveRecord.Model 的C#类库和一个名为Terminator.Practice阅读全文

    posted @ 2013-01-05 09:16 丶Terminator 阅读(687) | 评论 (3) 编辑

    设计模式学习日记一(持续更新)
    摘要: 写在前面:小弟的设计模式学习笔记,目的是记录最近一段时间学习的过程,大婶们请勿喷~~~不胜感激!!!GoF(Gang of Four)设计模式常见的设计模式原则和SOLID设计原则Fowler企业模式一、 设计模式释义设计模式是高层次的、抽象的解决方案模板。可以将这些模式视为解决方案的蓝本而不是解决方案本身。从中无法找到一种可以简单地运用到应用程序中的框架;相反,通常是通过重构自己的代码并将为题泛化来实现设计模式。设计模式的起源当前软件体系结构中比较流行的设计模式起源于程序员多年使用面向对象编程语言而积累的经验和知识。Design patterns: Elements of Reuable O阅读全文

    posted @ 2012-12-29 11:46 丶Terminator 阅读(125) | 评论 (2) 编辑

  • 相关阅读:
    Ubantu 安装Redis
    传说中的WCF(5):数据协定(a)
    传说中的WCF(4):发送和接收SOAP头
    传说中的WCF(3):多个协定
    传说中的WCF(2):服务协定的那些事儿
    传说中的WCF(1):这东西难学吗?
    Linq教程
    Installutil.exe的位置和路径
    uni-app中对输入框的判断与提示(密码格式为6-12位,必须有大小写字母和数字组成)
    uni-app系列回顾总结----项目国际化
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2848137.html
Copyright © 2020-2023  润新知