zoukankan      html  css  js  c++  java
  • Domain Model

    Domain Model案例,项目结构图

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

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

    ASPPatterns.Chap4.DomainModel.AppService:AppService项目充当应用程序的网关(API如果愿意的话)。表示层将通过消息(简单的数据传输对象)与AppService通信。AppService层还将定义试图模型,这些是领域模型的展开试图,只用于数据显示。

    ASPPatterns.Chap4.DomainModel.UI.Web:UI.Web项目负责应用程序的表示和用户体验需求。该项目只与AppService交互,并接收专门为用户体验视图创建的强类型视图模型。


    在一个项目中,业务逻辑最为重要,先看存放业务逻辑的Model项目

    在Model项目下创建Transaction.cs文件,Transaction对象是一个值对象。

    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.Model
    {
        public class Transaction
        {
            public Transaction(decimal deposit, decimal withdrawal, string reference, DateTime date)
            {
                this.Deposit = deposit;
                this.Withdrawal = withdrawal;
                this.Reference = reference;
                this.Date = date;
            }
    
            public decimal Deposit { get; internal set; }
            public decimal Withdrawal { get; internal set; }
            public string Reference { get; internal set; }
            public DateTime Date { get; internal set; }
        }
    }
    Transaction.cs

    再在Model项目下创建BankAccount对象。

    using System;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.Model
    {
        /// <summary>
        /// Withdraw CanWithdraw
        /// </summary>
        public class BankAccount
        {
            decimal _balance;
            Guid _accountNo;
            string _customerRef;
            IList<Transaction> _transactions;
            public BankAccount()
                : this(Guid.NewGuid(), 0, new List<Transaction>(), "")
            {
                _transactions.Add(new Transaction(0m, 0m, "account Created", DateTime.Now));
            }
    
            public Guid AccountNo { get { return _accountNo; } internal set { _accountNo = value; } }
    
            public decimal Balance { get { return _balance; } internal set { _balance = value; } }
    
            public string CustomerRef { get { return _customerRef; } set { _customerRef = value; } }
    
    
            public BankAccount(Guid id, decimal balance, IList<Transaction> transaction, string customerRef)
            {
                _accountNo = id;
                _balance = balance;
                _transactions = transaction;
                _customerRef = customerRef;
            }
    
            /// <summary>
            /// 当用户尝试从某个账号取回现金的时候,先使用CanWithdraw方法来判断该用户是否可以取钱。(Test-Doer模式,先测试,再执行)
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public bool CanWithdraw(decimal amount)
            {
                return (Balance >= amount);
            }
    
            /// <summary>
            /// 取款
            /// </summary>
            /// <param name="amount"></param>
            /// <param name="reference"></param>
            public void Withdraw(decimal amount, string reference)
            {
                if (CanWithdraw(amount))
                {
                    Balance -= amount;
                    _transactions.Add(new Transaction(0m, amount, reference, DateTime.Now));
                }
                else
                {
                    throw new InsufficientFundsException();//当用户没有足够的现金金额去取款的时候,就要抛出一个异常。
                }
            }
            /// <summary>
            /// 用户存款
            /// </summary>
            /// <param name="amount"></param>
            /// <param name="reference"></param>
            public void Deposit(decimal amount, string reference)
            {
                Balance += amount;
                _transactions.Add(new Transaction(0m, amount, reference, DateTime.Now));
            }
    
            public IEnumerable<Transaction> GetTransactions()
            {
                return _transactions;
            }
        }
    }
    BankAccount.cs

    当用户没有足够的现金金额去取款的时候,就要抛出一个异常。所以需要创建一个类来负责抛出异常。该类仍然处于Model下。

    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.Model
    {
        public class InsufficientFundsException:ApplicationException
        {
            
        }
    }
    InsufficientFundsException.cs

    现在需要某种方法来持久化BankAccount和Transactions。Model项目之定义接口,具体实现由Repository项目来实现。

    using System;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.Model
    {
        public interface IBankAccountRepository
        {
            void Add(BankAccount bankAccount);
            void Save(BankAccount bankAccount);
            IEnumerable<BankAccount> FindAll();
            BankAccount FindBy(Guid AccountId);
        }
    }
    IBankAccountRepository.cs

    有些动作没有很好地映射到领域实体的方法。对于这类情况,可以使用领域服务,在两个账号之间转账的动作就是一种属于服务类的责任。仍需要将BankAccountService定义在Model项目中。

    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.Model
    {
        public class BankAccountService
        {
            IBankAccountRepository _bankAccountRepository;
            public BankAccountService(IBankAccountRepository bankAccountRepository)
            {
                _bankAccountRepository = bankAccountRepository;
            }
    
            public void Transfer(Guid accountNoTo, Guid accountNoFrom, decimal amount)
            {
                BankAccount bankAccountTo = _bankAccountRepository.FindBy(accountNoTo);
                BankAccount bankAccountFrom = _bankAccountRepository.FindBy(accountNoFrom);
                if (bankAccountFrom.CanWithdraw(amount))
                {
                    bankAccountTo.Deposit(amount, "From Acc " + bankAccountFrom.CustomerRef + " ");
                    bankAccountFrom.Withdraw(amount, "Transfer To Acc " + bankAccountTo.CustomerRef + " ");
                    _bankAccountRepository.Save(bankAccountTo);
                    _bankAccountRepository.Save(bankAccountFrom);
                }
                else
                {
                    throw new InsufficientFundsException();
                }
            }
    
        }
    }
    BankAccountService.cs

     在BankAccountService的当前实现中,在保存两个银行账号之间发生的任何错误均会让数据处于非法状态。(使用unit of work来解决这个问题)


    Demo中的转账的功能大致如此,移步至Repository项目。

    编写用来持久化BankAccount和Transaction业务对象的方法。BankAccountRepository需要继承先前在Model中定义的接口IBankAccountRepository,并实现接口中定义的方法。

    using ASPPatterns.Chap4.DomainModel.Model;
    using System;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    
    namespace ASPPatterns.Chap4.DomainModel.Repository
    {
        public class BankAccountRepository : IBankAccountRepository
        {
            private string _conStr;
            public BankAccountRepository()
            {
                _conStr = "";//可以从配置文件中获取数据库连接字符串
            }
            public void Add(BankAccount bankAccount)
            {
                string insertSql = string.Format("Insert into BankAccounts (bankAccountId,balance,customerRef) values('{0}','{1}','{2}')", bankAccount.AccountNo, bankAccount.Balance, bankAccount.CustomerRef);
                using (SqlConnection connection = new SqlConnection(_conStr))
                {
                    SqlCommand command = connection.CreateCommand();
                    command.CommandText = insertSql;
                    connection.Open();
                    command.ExecuteNonQuery();
                }
            }
    
    
    
            public void Save(BankAccount bankAccount)
            {
                string bankAccountUpdateSql = string.Format("update bankAccounts set balance='{0}',customerRef='{1}' where bankAccountID='{2}'", bankAccount.Balance, bankAccount.CustomerRef, bankAccount.AccountNo);
                using (SqlConnection connection = new SqlConnection(_conStr))
                {
                    SqlCommand command = connection.CreateCommand();
                    command.CommandText = bankAccountUpdateSql;
                    connection.Open();
                    command.ExecuteNonQuery();
                }
            }
    
            public IEnumerable<BankAccount> FindAll()
            {
                IList<BankAccount> accounts = new List<BankAccount>();
                string queryStr = "select * from transactions t inner join bankaccounts b on t.bankaccountID=b.bankAccountid order by b.bankaccountid";
                using (SqlConnection connection = new SqlConnection(_conStr))
                {
                    SqlCommand command = connection.CreateCommand();
                    command.CommandText = queryStr;
                    connection.Open();
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        //做映射,将查询到的数据赋值给accounts
                    }
                }
                return accounts;
            }
    
            public BankAccount FindBy(Guid AccountId)
            {
                BankAccount account = null;
                string sqlCmd = string.Format("select * from transaction t inner join bankaccount b on t.bankaccountid=b.bankaccountid where b.bankaccountid='{0}'", AccountId);
                using (SqlConnection connection = new SqlConnection(_conStr))
                {
                    SqlCommand command = connection.CreateCommand();
                    command.CommandText = sqlCmd;
                    //
    
                }
                return account;
            }
        }
    }
    BankAccountRepository.cs

    做好持久化方面的工作后,再看Service层

    在AppService层内创建文件夹ViewModel,并在该文件夹中添加两个视图模型BankAccountView和TransactionView。这两个类的作用是用于界面显示,当BankAccount和Transaction对象中有数据,并要显示在界面上的时候,会使用映射器将BankAccount和Transaction对象中的数据映射到BankAccountView和TransactionView对象上,Demo中是手写了一个映射器,项目中可以使用AutoMapper在ASP.NET MVC中,结合razor语言,将数据显示在界面上。

    using System;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.ViewModel
    {
        public class BankAccountView
        {
            public Guid AccountNo { get; set; }
            public string Balance { get; set; }
            public string CustomerRef { get; set; }
            public IList<TransactionView> Transactions { get; set; }
        }
    }
    BankAccountView.cs
    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.ViewModel
    {
        public class TransactionView
        {
            public string Deposit { get; set; }
            public string Withdrawal { get; set; }
            public string Reference { get; set; }
            public DateTime Date { get; set; }
        }
    }
    TransactionView.cs

    映射器ViewMapper

    using ASPPatterns.Chap4.DomainModel.AppService.ViewModel;
    using ASPPatterns.Chap4.DomainModel.Model;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService
    {
        public static class ViewMapper
        {
            public static TransactionView CreateTransactionViewFrom(Transaction tran)
            {
                return new TransactionView
                {
                    Deposit = tran.Deposit.ToString(),
                    Withdrawal = tran.Withdrawal.ToString(),
                    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>()
                };
            }
        }
    }
    ViewMapper

    再在Appservice层下创建一个Messages文件夹,该文件夹中包含了所有用来与服务层通信的请求-应答对象。使用Messaging(消息传送)模式为了使让所有的API对外返回的信息格式统一。

    先创建基类ResponseBase

    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public abstract class ResponseBase
        {
            public bool Success { get; set; }
            public string Message { get; set; }
        }
    }
    ResponseBase.cs

    创建所有的,需要实现请求和应答的对象

    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class BankAccountCreateRequest
        {
            public string CustomerName { get; set; }
        }
    }
    BankAccountCreateRequest.cs
    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class BankAccountCreateReponse : ResponseBase
        {
            public Guid BankAccountId { get; set; }
        }
    }
    BankAccountCreateReponse
    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class DepositRequest
        {
            public Guid AccountId { get; set; }
            public decimal Amount { get; set; }
        }
    }
    DepositRequest.cs
    using ASPPatterns.Chap4.DomainModel.AppService.ViewModel;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class FindAllBankAccountResponse : ResponseBase
        {
            public IList<BankAccountView> BankAccounView { get; set; }
        }
    }
    FindAllBankAccountResponse.cs
    using ASPPatterns.Chap4.DomainModel.AppService.ViewModel;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class FindBankAccountResponse
        {
            public BankAccountView BankAccount { get; set; }
        }
    }
    FindBankAccountResponse.cs
    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class TransferRequest
        {
            public Guid AccountIdTo { get; set; }
            public Guid AccountIdFrom { get; set; }
            public decimal Amount { get; set; }
    
        }
    }
    TransferRequest.cs
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class TransferResponse : ResponseBase
        {
    
        }
    }
    TransferResponse.cs
    using System;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService.Messages
    {
        public class WithdrawalRequest
        {
            public Guid AccountId { get; set; }
            public decimal Amount { get; set; }
        }
    }
    WithdrawalRequest.cs

     ApplicationBankAccountService类协调应用程序活动并将所有的业务任务委托给领域模型。该层并不包含任何业务逻辑,有助于防止任何与业务无关的代码污染领域模型项目。

    该层还将领域实体转换成数据传输对象,从而保护领域的内部操作,并为一起工作的表示层提供了一个易于使用的API。

    using ASPPatterns.Chap4.DomainModel.AppService.Messages;
    using ASPPatterns.Chap4.DomainModel.AppService.ViewModel;
    using ASPPatterns.Chap4.DomainModel.Model;
    using ASPPatterns.Chap4.DomainModel.Repository;
    using System;
    using System.Collections.Generic;
    
    namespace ASPPatterns.Chap4.DomainModel.AppService
    {
        /// <summary>
        /// 该类协调应用程序活动,并将所有的业务任务委托给领域模型。该层并不包含任何业务逻辑。有助于防止任何与业务无关的代码污染领域模型项目。
        /// 该层还将领域实体转换成数据传输对象,从而保护领域内部操作,并为一起工作的表示层提供了一个易于使用的API
        /// </summary>
        public class ApplicationBankAccountService
        {
            BankAccountService _bankAccountService;
            IBankAccountRepository _bankRepository;
    
            public ApplicationBankAccountService() : this(new BankAccountRepository(), new BankAccountService(new BankAccountRepository()))
            {
    
            }
            public ApplicationBankAccountService(IBankAccountRepository bankRepository, BankAccountService bankAccountService)
            {
                _bankRepository = bankRepository;
                _bankAccountService = bankAccountService;
            }
    
            public BankAccountCreateReponse CreateBankAccount(BankAccountCreateRequest bankAccountCreateRequest)
            {
                BankAccountCreateReponse bankAccountCreateReponse = new BankAccountCreateReponse();
                BankAccount bankAccount = new BankAccount(new Guid(), 12.12M, new List<Transaction>(), "customerRef");
                bankAccount.CustomerRef = bankAccountCreateRequest.CustomerName;
                _bankRepository.Add(bankAccount);
                return bankAccountCreateReponse;
            }
    
            public void Deposit(DepositRequest depositRequest)
            {
                BankAccount bankAccount = _bankRepository.FindBy(depositRequest.AccountId);
                bankAccount.Deposit(depositRequest.Amount, "");
                _bankRepository.Save(bankAccount);
            }
    
            public void Withdrawal(WithdrawalRequest withdrawalRequest)
            {
                BankAccount bankAccount = _bankRepository.FindBy(withdrawalRequest.AccountId);
                bankAccount.Withdraw(withdrawalRequest.Amount, "");
                _bankRepository.Save(bankAccount);
            }
    
            public TransferResponse Transfer(TransferRequest request)
            {
                TransferResponse response = new TransferResponse();
                try
                {
                    _bankAccountService.Transfer(request.AccountIdTo, request.AccountIdFrom, request.Amount);
                    response.Success = true;
                }
                catch (Exception)
                {
                    response.Message = "There is not enough funds in account no:" + request.AccountIdFrom.ToString();
                    response.Success = false;
                }
                return response;
            }
    
            public FindAllBankAccountResponse GetAllBankAccounts()
            {
                FindAllBankAccountResponse findAllBankAccountResponse = new FindAllBankAccountResponse();
                IList<BankAccountView> bankAccountViews = new List<BankAccountView>();
                findAllBankAccountResponse.BankAccounView = bankAccountViews;
                foreach (BankAccount acc in _bankRepository.FindAll())
                {
                    bankAccountViews.Add(ViewMapper.CreateBankAccountViewFrom(acc));
                }
                return findAllBankAccountResponse;
            }
    
            public FindBankAccountResponse GetBankAccountBy(Guid Id)
            {
                FindBankAccountResponse bankAccountResponse = new FindBankAccountResponse();
                BankAccount acc = _bankRepository.FindBy(Id);
                BankAccountView bankAccountView = ViewMapper.CreateBankAccountViewFrom(acc);
                foreach (Transaction tran in acc.GetTransactions())
                {
                    bankAccountView.Transactions.Add(ViewMapper.CreateTransactionViewFrom(tran));
                }
                bankAccountResponse.BankAccount = bankAccountView;
                return bankAccountResponse;
            }
        }
    }
    ApplicationBankAccountService.cs

    在UI层调用Service。UI曾的界面没有展示,那不是重点。重点考察如何在UI层中调用Service。

    using ASPPatterns.Chap4.DomainModel.AppService;
    using ASPPatterns.Chap4.DomainModel.AppService.Messages;
    using ASPPatterns.Chap4.DomainModel.AppService.ViewModel;
    using System;
    using System.Web.UI;
    
    namespace ASPPatterns.Chap4.DomainModel.UI.Web
    {
        public partial class Default : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!Page.IsPostBack)
                {
                    ShowAllAccounts();
                }
            }
    
            private void ShowAllAccounts()
            {
                ddlBankAccounts.Items.Clear();
                FindAllBankAccountResponse response = new ApplicationBankAccountService().GetAllBankAccounts();
                ddlBankAccounts.Items.Add(new ListItem("Select An Account", ""));
                foreach (BankAccountView accView in response.BankAccounView)
                {
                    ddlBankAccounts.Items.Add(new ListItem(accView.CustomerRef, accView.AccountNo.ToString()));
                }
            }
    
            protected void btCreateAccount_Click(object sender, EventArgs e)
            {
                BankAccountCreateRequest createAccountRequest = new BankAccountCreateRequest();
                ApplicationBankAccountService service = new ApplicationBankAccountService();
                service.CreateBankAccount(createAccountRequest);
                ShowAllAccounts();
            }
    
            protected void ddlBankAccounts_SelectedIndexChanged(object sender, EventArgs e)
            {
                DisplaySelectedAccount();
            }
    
            private void DisplaySelectedAccount()
            {
                if (ddlBankAccounts.SelectedValue.ToString() != "")
                {
                    ApplicationBankAccountService service = new ApplicationBankAccountService();
                    FindBankAccountResponse response = service.GetBankAccountBy(new Guid());
                    BankAccountView accView = response.BankAccount;
    
                    lblAccount.Text = accView.Balance.ToString();
                    lblBalance.Text = accView.Balance.ToString();
                    lblCustomerRef.Text = accView.CustomerRef;
    
                    rptTransactions.DataSource = accView.Transactions;
                    rptTransactions.DataBind();
    
                    FindAllBankAccountResponse allAccountResponse = service.GetAllBankAccounts();
                    ddlBankAccountsToTransferTo.Items.Clear();
    
                    foreach (var acc in allAccountResponse.BankAccounView)
                    {
                        if (acc.AccountNo.ToString() != ddlBankAccounts.SelectedValue.ToString())
                            ddlBankAccountsToTransferTo.Items.Add(new ListItem(acc.CustomerRef, acc.AccountNo.ToString()));
                    }
                }
            }
    
            protected void btnWithdrawal_Click(object sender, EventArgs e)
            {
                ApplicationBankAccountService service = new ApplicationBankAccountService();
                WithdrawalRequest request = new WithdrawalRequest();
                Guid guid = new Guid();
                request.AccountId = guid;
                request.Amount = decimal.Parse(txtAmount.Text);
                service.Withdrawal(request);
                DisplaySelectedAccount();
            }
    
            protected void btnDeposit_Click(object sender, EventArgs e)
            {
                ApplicationBankAccountService service = new ApplicationBankAccountService();
                DepositRequest request = new DepositRequest();
                Guid guid = new Guid();
                request.AccountId = guid;
                request.Amount = decimal.Parse(txtAmount.Text);
                service.Deposit(request);
                DisplaySelectedAccount();
            }
        }
    }
    Default.cs
  • 相关阅读:
    DHCP 工作原理
    网络端口知识总结
    EAPOL 协议
    hostapd源代码分析(三):管理帧的收发和处理
    hostapd源代码分析(二):hostapd的工作机制
    hostapd源代码分析(一):网络接口和BSS的初始化
    Hostapd
    事件属性
    CSS颜色和长度单位
    弹性盒子FlexBox简介(一)
  • 原文地址:https://www.cnblogs.com/vichin/p/13048459.html
Copyright © 2011-2022 走看看