c#领域驱动设计
代码是个程序员都能写,
怎么的代码才是好的,
--------------------------------------------------------------------------------------------------
1.设计
1.可靠性和可得性
数据库有问题的时候,一般都是系统重启,新系统数据系统应该能够在使用过程中备份,而不应该轻易发生错误。
2.伸缩性
系统应该能够处理用户源源不断的需求。
3.可维护性
系统代码重复改一个需求到处粘贴复制,这种情况下必须考虑重构,领域逻辑处于核心位置,而且绝对不重复,可复用。
2.设计架构层
1.基础设施层
该层被所有的层所引用,定义所有的基础设施所需的方法。
2.应用层
主要维护任务的状态,负责不同领域对象之间的协调动作,该层的方法大多都是静态的
3.领域层
该层主要负责业务逻辑
4.持久层
该层主要负责数据持久化(与数据库打交到)
5.UI层
页面呈现
6.数据传输层
数据实体对象(负责数据运输的实体对象)
基础设施层
1. 定义一个实体接口 IEntity
定义一个实体接口标识该对象为实体对象(并且标识主键key)
/// <summary>
/// 实体接口
/// </summary>
public interface IEntity
{
/// <summary>
/// key
/// </summary>
object Key { get; }
}
2.抽象一个分层超类型 EntityBase
抽象一个基类所有的领域模型都将继承它以获得他们的标识
/// <summary>
/// 实体抽象类
/// </summary>
public abstract class EntityBase : IEntity
{
/// <summary>
/// 标识key
/// </summary>
private readonly object key; /// <summary>
/// Initializes a new instance of the <see cref="EntityBase"/> class.
/// 默认构造函数 default constructor
/// </summary>
protected EntityBase()
: this(null)
{
} /// <summary>
/// Initializes a new instance of the <see cref="EntityBase"/> class.
/// </summary>
/// <param name="key">key</param>
protected EntityBase(object key)
{
this.key = key;
} /// <summary>
/// Gets the key.
/// </summary>
public object Key
{
get
{
return this.key;
}
} /// <summary>
/// The equals.
/// </summary>
/// <param name="other">
/// The other.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
protected bool Equals(EntityBase other)
{
return Equals(this.Key, other.Key);
} /// <summary>
/// The equals.
/// </summary>
/// <param name="entity">
/// The entity.
/// </param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public override bool Equals(object entity)
{
if (entity == null || !(entity is EntityBase))
{
return false;
} return this == (EntityBase)entity;
} /// <summary>
/// The get hash code.
/// </summary>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public override int GetHashCode()
{
return this.Key != null ? this.Key.GetHashCode() : ;
} /// <summary>
/// The ==.
/// </summary>
/// <param name="left">
/// The base 1.
/// </param>
/// <param name="right">
/// The base 2.
/// </param>
/// <returns>
/// </returns>
public static bool operator ==(EntityBase left, EntityBase right)
{
if ((object)left == null && (object)right == null)
{
return true;
} if ((object)left == null || (object)right == null)
{
return false;
} return left.Key == right.Key;
} /// <summary>
/// The !=.
/// </summary>
/// <param name="left">
/// The base 1.
/// </param>
/// <param name="right">
/// The base 2.
/// </param>
/// <returns>
/// </returns>
public static bool operator !=(EntityBase left, EntityBase right)
{
return !(left == right);
}
}
做一个泛型的标识继承EntityBase
/// <summary>
/// The entity.
/// </summary>
/// <typeparam name="T">
/// </typeparam>
public abstract class EntityBase<T> : EntityBase
{
/// <summary>
/// Initializes a new instance of the <see cref="EntityBase{T}"/> class.
/// Initializes a new instance of the <see cref="EntityBase"/> class.
/// 默认构造函数 default constructor
/// </summary>
protected EntityBase()
: base(null)
{
} /// <summary>
/// Initializes a new instance of the <see cref="EntityBase{T}"/> class.
/// Initializes a new instance of the <see cref="EntityBase"/> class.
/// </summary>
/// <param name="key">
/// key
/// </param>
protected EntityBase(T key)
: base(key)
{
} /// <summary>
/// Gets the key.
/// </summary>
public new T Key
{
get
{
T @default = default(T);
if (base.Key == null)
{
return @default;
} return (T)base.Key;
}
}
}
3. 仓储接口 IRepository<in TKey, TValue>
仓储接口提供增删查改的方法签名,并且多了一个索引 TValue类型并且标识为IEntity
/// <summary>
/// 仓储接口
/// </summary>
/// <typeparam name="TKey">TKey</typeparam>
/// <typeparam name="TValue">TValue</typeparam>
public interface IRepository<in TKey, TValue>
where TValue : EntityBase<TKey>
{
/// <summary>
/// 根据key查询
/// </summary>
/// <param name="key">key</param>
/// <returns>TValue</returns>
TValue FindBy(TKey key); /// <summary>
/// 添加
/// </summary>
/// <param name="item">item</param>
void Add(TValue item); /// <summary>
/// 索引查询
/// </summary>
/// <param name="key">key</param>
/// <returns>TValue</returns>
TValue this[TKey key] { get; set; } /// <summary>
/// 删除
/// </summary>
/// <param name="item">item</param>
void Remove(TValue item); /// <summary>
/// 更新
/// </summary>
/// <param name="item">item</param>
void Update(TValue item);
}
4.仓储工厂
通过配置文件创建仓储(配置类就不多说,稍后附上源码),为了决定创建哪一种仓储,RespositoryFactory类使用泛型类型参数。
/// <summary>
/// 仓储工厂
/// </summary>
public static class RepositoryFactory
{
/// <summary>
/// Dictionary to enforce the singleton pattern
/// </summary>
private static readonly Dictionary<string, object> m_respository = new Dictionary<string, object>(); /// <summary>
/// The get repository.
/// </summary>
/// <typeparam name="TRepository">
/// </typeparam>
/// <typeparam name="TEntity">
/// </typeparam>
/// <returns>
/// The <see cref="TRepository"/>.
/// </returns>
public static TRepository GetRepository<TRepository, TEntity>()
where TRepository : class, IRepository<TEntity>
where TEntity : EntityBase
{
TRepository respository = default(TRepository);
string interfaceShortName = typeof(TRepository).Name;
if (!m_respository.ContainsKey(interfaceShortName))
{
RepositorySettings settings = (RepositorySettings)ConfigurationManager.GetSection(RepositoryMappingConstants.RepositoryMappingsConfigurationSectionName);
string repositoryFullTypeName = settings.RepositoryMappings[interfaceShortName].RepositoryFullTypeName;
Type type = Type.GetType(repositoryFullTypeName);
if (type != null)
{
respository = Activator.CreateInstance(type) as TRepository;
m_respository.Add(interfaceShortName, respository);
}
}
else
{
respository = (TRepository)m_respository[interfaceShortName];
} return respository;
}
}
5.工作单元
由于需要同时操作几个仓储(同时更新订单状态,订单详细信息)要求操作结果一致。
调用折注册---对象的用户必须记得注册到工作单元
对象注册-----对象把自身注册到工作单元
工作单元仓储接口
主要定义仓储的3个基本操作签名
/// <summary>
/// 工作单元仓储接口
/// </summary>
public interface IUnitOfWorkRepository
{
/// <summary>
/// 持久化新增实体
/// </summary>
/// <param name="item">待新增实体接口</param>
void PersistNewItem(IEntity item); /// <summary>
/// 持久化更新实体
/// </summary>
/// <param name="item">待更新实体接口</param>
void PersistUpdatedItem(IEntity item); /// <summary>
/// 持久化删除实体
/// </summary>
/// <param name="item">待删除实体接口</param>
void PersistDeletedItem(IEntity item);
}
工作单元接口
/// <summary>
/// InvokeMethod
/// </summary>
/// <param name="entity">
/// The entity.
/// </param>
public delegate void InvokeMethod(IEntity entity); /// <summary>
/// 工作单元接口
/// </summary>
public interface IUnitOfWork : IDisposable
{
/// <summary>
/// .NET Framework 数据提供程序
/// </summary>
IDbCommand Command { get; } /// <summary>
/// 提交工作单元
/// </summary>
void Complete(); /// <summary>
/// 回滚工作单元
/// </summary>
void Rollback(); /// <summary>
/// 注册新增实体工作单元仓储接口
/// </summary>
/// <param name="entity">待新增实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository); /// <summary>
/// 注册修改实体工作单元仓储接口
/// </summary>
/// <param name="entity">待修改实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository); /// <summary>
/// 注册删除实体工作单元仓储接口
/// </summary>
/// <param name="entity">待删除实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository); /// <summary>
/// 注册一个其他非基础的增删改工作单元仓储接口
/// </summary>
/// <param name="entity">待操作实体接口</param>
/// <param name="methodName">自定义委托</param>
void RegisterInvokeMethod(IEntity entity, InvokeMethod methodName); /// <summary>
/// 注册一个非继承聚合根的其他非基础的增删改工作单元仓储接口
/// </summary>
/// <param name="entity">待操作实体接口</param>
/// <param name="methodName">Func委托</param>
void RegisterInvokeMethod(object entity, Func<object, object> methodName);
}
工作单元
/// <summary>
/// 工作单元
/// </summary>
public class UnitOfWork : IUnitOfWork
{
/// <summary>
/// 新增实体工作单元
/// </summary>
private readonly Dictionary<IEntity, IUnitOfWorkRepository> m_addedEntities; /// <summary>
/// 修改实体工作单元
/// </summary>
private readonly Dictionary<IEntity, IUnitOfWorkRepository> m_changedEntities; /// <summary>
/// 删除实体工作单元
/// </summary>
private readonly Dictionary<IEntity, IUnitOfWorkRepository> m_deletedEntities; /// <summary>
/// 其他非基础的增删改
/// </summary>
private readonly Dictionary<IEntity, InvokeMethod> m_invokeEntities; /// <summary>
/// 非继承聚合根的其他非基础的增删改工作单元
/// </summary>
private readonly Dictionary<object, Func<object, object>> m_func; /// <summary>
/// IDbConnection
/// </summary>
private IDbConnection m_connection; /// <summary>
/// IDbCommand
/// </summary>
private IDbCommand m_command; /// <summary>
/// IDbTransaction
/// </summary>
private IDbTransaction m_trans; /// <summary>
/// Initializes a new instance of the <see cref="UnitOfWork"/> class.
/// </summary>
/// <param name="connectionSetting">
/// The connection setting.
/// </param>
public UnitOfWork(string connectionSetting)
{
this.m_connection = DbFactories.GetConnection(connectionSetting);
this.m_command = this.m_connection.CreateCommand();
this.m_trans = this.m_connection.BeginTransaction();
this.m_command.Transaction = this.m_trans;
this.m_addedEntities = new Dictionary<IEntity, IUnitOfWorkRepository>();
this.m_changedEntities = new Dictionary<IEntity, IUnitOfWorkRepository>();
this.m_deletedEntities = new Dictionary<IEntity, IUnitOfWorkRepository>();
this.m_invokeEntities = new Dictionary<IEntity, InvokeMethod>();
this.m_func = new Dictionary<object, Func<object, object>>();
} /// <summary>
/// .NET Framework 数据提供程序
/// </summary>
public IDbCommand Command
{
get
{
return this.m_command;
}
} /// <summary>
/// 提交工作单元
/// </summary>
public void Complete()
{
try
{
foreach (IEntity entity in this.m_deletedEntities.Keys)
{
this.m_deletedEntities[entity].PersistDeletedItem(entity);
} foreach (IEntity entity in this.m_addedEntities.Keys)
{
this.m_addedEntities[entity].PersistNewItem(entity);
} foreach (IEntity entity in this.m_changedEntities.Keys)
{
this.m_changedEntities[entity].PersistUpdatedItem(entity);
} foreach (IEntity entity in this.m_invokeEntities.Keys)
{
this.m_invokeEntities[entity](entity);
} foreach (var entity in this.m_func)
{
entity.Value(entity.Key);
} this.m_trans.Commit();
}
catch (Exception)
{
this.Rollback();
}
finally
{
this.Dispose();
this.Clear();
}
} /// <summary>
/// 回滚工作单元
/// </summary>
public void Rollback()
{
this.m_trans.Rollback();
} /// <summary>
/// 注册新增实体工作单元仓储接口
/// </summary>
/// <param name="entity">待新增实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository)
{
this.m_addedEntities.Add(entity, repository);
} /// <summary>
/// 注册修改实体工作单元仓储接口
/// </summary>
/// <param name="entity">待修改实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository)
{
this.m_changedEntities.Add(entity, repository);
} /// <summary>
/// 注册删除实体工作单元仓储接口
/// </summary>
/// <param name="entity">待删除实体接口</param>
/// <param name="repository">工作单元仓储接口</param>
public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository)
{
this.m_deletedEntities.Add(entity, repository);
} /// <summary>
/// 注册一个其他非基础的增删改工作单元仓储接口
/// </summary>
/// <param name="entity">待操作实体接口</param>
/// <param name="methodName">自定义委托</param>
public void RegisterInvokeMethod(IEntity entity, InvokeMethod methodName)
{
this.m_invokeEntities.Add(entity, methodName);
} /// <summary>
/// 注册一个非继承聚合根的其他非基础的增删改工作单元仓储接口
/// </summary>
/// <param name="entity">待操作实体接口</param>
/// <param name="methodName">Func委托</param>
public void RegisterInvokeMethod(object entity, Func<object, object> methodName)
{
this.m_func.Add(entity, methodName);
} /// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (this.m_trans != null)
{
this.m_trans.Dispose();
this.m_trans = null;
} if (this.m_command != null)
{
this.m_command.Dispose();
this.m_command = null;
} if (this.m_connection != null)
{
this.m_connection.Dispose();
this.m_connection.Close();
this.m_connection = null;
}
} /// <summary>
/// 清除
/// </summary>
private void Clear()
{
this.m_addedEntities.Clear();
this.m_changedEntities.Clear();
this.m_deletedEntities.Clear();
this.m_invokeEntities.Clear();
this.m_func.Clear();
}
}
6.仓储基类
主要是为了消除大量重复代码,仓储将从基类继承共同的代码,仓储基类会实现仓储接口IRepository<in TKey, TValue> 并且实现IUnitOfWorkRepository工作单元仓储接口
/// <summary>
/// The repository base.
/// </summary>
/// <typeparam name="TKey">
/// </typeparam>
/// <typeparam name="TValue">
/// </typeparam>
public abstract class RepositoryBase<TKey, TValue> : IRepository<TKey, TValue>, IUnitOfWorkRepository
where TValue : EntityBase<TKey>
{
/// <summary>
/// IUnitOfWork
/// </summary>
public IUnitOfWork UnitOfWork { get; private set; } /// <summary>
/// Initializes a new instance of the <see cref="RepositoryBase{TKey,TValue}"/> class.
/// </summary>
protected RepositoryBase()
: this(null)
{
} /// <summary>
/// Initializes a new instance of the <see cref="RepositoryBase{TKey,TValue}"/> class.
/// </summary>
/// <param name="unitOfWork">
/// The unit of work.
/// </param>
protected RepositoryBase(IUnitOfWork unitOfWork)
{
this.UnitOfWork = unitOfWork;
} /// <summary>
/// The find by.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <returns>
/// The <see cref="TValue"/>.
/// </returns>
public abstract TValue FindBy(TKey key); /// <summary>
/// The add.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public void Add(TValue item)
{
if (this.UnitOfWork != null)
{
this.UnitOfWork.RegisterAdded(item, this);
}
} /// <summary>
/// The this.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <returns>
/// The <see cref="TValue"/>.
/// </returns>
public TValue this[TKey key]
{
get
{
return this.FindBy(key);
} set
{
if (this.FindBy(key) == null)
{
this.Add(value);
}
else
{
this.Update(value);
}
}
} /// <summary>
/// The remove.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public void Remove(TValue item)
{
if (this.UnitOfWork != null)
{
this.UnitOfWork.RegisterRemoved(item, this);
}
} /// <summary>
/// The update.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public void Update(TValue item)
{
if (this.UnitOfWork != null)
{
this.UnitOfWork.RegisterChanged(item, this);
}
} /// <summary>
/// The persist new item.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public abstract void PersistNewItem(IEntity item); /// <summary>
/// The persist updated item.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public abstract void PersistUpdatedItem(IEntity item); /// <summary>
/// The persist deleted item.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public abstract void PersistDeletedItem(IEntity item);
}
7.SqlCe仓储基类
主要是一些操作数据库的方法,这样避免了使用DBHelper类
/// <summary>
/// The sql ce repository base.
/// </summary>
/// <typeparam name="TKey">
/// </typeparam>
/// <typeparam name="TValue">
/// </typeparam>
public abstract class SqlCeRepositoryBase<TKey, TValue> : RepositoryBase<TKey, TValue>, IDisposable
where TValue : EntityBase<TKey>
{
/// <summary>
/// The connection.
/// </summary>
protected IDbConnection Connection { get; private set; } /// <summary>
/// The cmd.
/// </summary>
protected IDbCommand Command { get; private set; } /// <summary>
/// Initializes a new instance of the <see cref="SqlCeRepositoryBase{TKey,TValue}"/> class.
/// </summary>
/// <param name="connectionSetting">
/// The connection setting.
/// </param>
protected SqlCeRepositoryBase(string connectionSetting)
: this(null, connectionSetting)
{
} /// <summary>
/// Initializes a new instance of the <see cref="SqlCeRepositoryBase{TKey,TValue}"/> class.
/// </summary>
/// <param name="unitOfWork">
/// The unit of work.
/// </param>
/// <param name="connectionSetting">
/// The connection setting.
/// </param>
protected SqlCeRepositoryBase(IUnitOfWork unitOfWork, string connectionSetting)
: base(unitOfWork)
{
if (UnitOfWork != null)
{
this.Command = UnitOfWork.Command;
}
else
{
if (this.Connection == null)
{
this.Connection = DbFactories.GetConnection(connectionSetting);
}
if (this.Connection.State != ConnectionState.Open)
{
this.Connection.Open();
}
this.Command = Connection.CreateCommand();
}
} #region Parameter /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name)
{
IDbDataParameter param = this.Command.CreateParameter();
param.ParameterName = name;
return param;
} /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name, object value)
{
IDbDataParameter param = CreateParameter(name);
param.Value = value ?? DBNull.Value;
return param;
} /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name, object value, DbType type)
{
IDbDataParameter param = CreateParameter(name, value);
param.DbType = type;
return param;
} /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name, object value, DbType type, ParameterDirection direction)
{
IDbDataParameter param = CreateParameter(name, value, type);
param.Direction = direction;
return param;
} /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <param name="size">
/// The size.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name, object value, DbType type, ParameterDirection direction, int size)
{
IDbDataParameter param = CreateParameter(name, value, type, direction);
param.Size = size;
return param;
} /// <summary>
/// The create parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <param name="size">
/// The size.
/// </param>
/// <param name="scale">
/// The scale.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
private IDbDataParameter CreateParameter(string name, object value, DbType type, ParameterDirection direction, int size, byte scale)
{
IDbDataParameter param = CreateParameter(name, value, type, direction, size);
param.Scale = scale;
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name)
{
IDbDataParameter param = CreateParameter(name);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name, object value)
{
IDbDataParameter param = CreateParameter(name, value);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name, object value, DbType type)
{
IDbDataParameter param = CreateParameter(name, value, type);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name, object value, DbType type, ParameterDirection direction)
{
IDbDataParameter param = CreateParameter(name, value, type, direction);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <param name="size">
/// The size.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name, object value, DbType type, ParameterDirection direction, int size)
{
IDbDataParameter param = CreateParameter(name, value, type, direction, size);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The add parameter.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="direction">
/// The direction.
/// </param>
/// <param name="size">
/// The size.
/// </param>
/// <param name="scale">
/// The scale.
/// </param>
/// <returns>
/// The <see cref="IDbDataParameter"/>.
/// </returns>
protected IDbDataParameter AddParameter(string name, object value, DbType type, ParameterDirection direction, int size, byte scale)
{
IDbDataParameter param = CreateParameter(name, value, type, direction, size, scale);
this.Command.Parameters.Add(param);
return param;
} /// <summary>
/// The clear parameters.
/// </summary>
protected void ClearParameters()
{
this.Command.Parameters.Clear();
} #endregion #region ExecuteReader /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected virtual IDataReader ExecuteReader(string sql, CommandType type, CommandBehavior behavior, int timeout)
{
if (string.IsNullOrWhiteSpace(sql))
{
throw new ArgumentNullException("sql");
}
this.Command.CommandText = sql;
this.Command.CommandType = type;
this.Command.CommandTimeout = timeout;
return this.Command.ExecuteReader(behavior);
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, CommandType type, CommandBehavior behavior)
{
return this.ExecuteReader(sql, type, behavior, );
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, CommandType type, int timeout)
{
return this.ExecuteReader(sql, type, CommandBehavior.Default, timeout);
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, CommandType type)
{
return this.ExecuteReader(sql, type, CommandBehavior.Default, );
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, CommandBehavior behavior, int timeout)
{
return this.ExecuteReader(sql, CommandType.Text, behavior, timeout);
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, CommandBehavior behavior)
{
return this.ExecuteReader(sql, CommandType.Text, behavior, );
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql, int timeout)
{
return this.ExecuteReader(sql, CommandType.Text, CommandBehavior.Default, timeout);
} /// <summary>
/// The execute reader.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The <see cref="IDataReader"/>.
/// </returns>
protected IDataReader ExecuteReader(string sql)
{
return this.ExecuteReader(sql, CommandType.Text, CommandBehavior.Default, );
} #endregion #region ExecuteTable /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected virtual DataTable ExecuteTable(string sql, CommandType type, CommandBehavior behavior, int timeout)
{
using (IDataReader dr = ExecuteReader(sql, type, behavior, timeout))
{
DataTable dt = new DataTable();
dt.Load(dr);
return dt;
}
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, CommandType type, CommandBehavior behavior)
{
return this.ExecuteTable(sql, type, behavior, );
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, CommandType type, int timeout)
{
return this.ExecuteTable(sql, type, CommandBehavior.Default, timeout);
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, CommandType type)
{
return this.ExecuteTable(sql, type, CommandBehavior.Default, );
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, CommandBehavior behavior, int timeout)
{
return this.ExecuteTable(sql, CommandType.Text, behavior, timeout);
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="behavior">
/// The behavior.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, CommandBehavior behavior)
{
return this.ExecuteTable(sql, CommandType.Text, behavior, );
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql, int timeout)
{
return this.ExecuteTable(sql, CommandType.Text, CommandBehavior.Default, timeout);
} /// <summary>
/// The execute table.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The <see cref="DataTable"/>.
/// </returns>
protected DataTable ExecuteTable(string sql)
{
return this.ExecuteTable(sql, CommandType.Text, CommandBehavior.Default, );
} #endregion #region ExecuteDataSet /// <summary>
/// The execute data set.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="tableName">
/// The table name.
/// </param>
/// <returns>
/// The <see cref="DataSet"/>.
/// </returns>
public DataSet ExecuteDataSet(string sql, params string[] tableName)
{
return this.ExecuteDataSet(sql, CommandType.Text, tableName);
} /// <summary>
/// The execute data set.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="tableName">
/// The table name.
/// </param>
/// <returns>
/// The <see cref="DataSet"/>.
/// </returns>
public virtual DataSet ExecuteDataSet(string sql, CommandType type, params string[] tableName)
{
using (IDataReader dr = this.ExecuteReader(sql, type, CommandBehavior.Default, ))
{
DataSet ds = new DataSet();
ds.Load(dr, LoadOption.Upsert, tableName);
return ds;
}
} #endregion #region ExecuteScalar /// <summary>
/// The execute scalar.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
public virtual object ExecuteScalar(string sql, CommandType type, int timeout)
{
if (string.IsNullOrWhiteSpace(sql))
{
throw new ArgumentNullException("sql");
}
this.Command.CommandText = sql;
this.Command.CommandType = type;
this.Command.CommandTimeout = timeout;
return this.Command.ExecuteScalar();
} /// <summary>
/// The execute scalar.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
public object ExecuteScalar(string sql, CommandType type)
{
return this.ExecuteScalar(sql, type, );
} /// <summary>
/// The execute scalar.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
public object ExecuteScalar(string sql, int timeout)
{
return this.ExecuteScalar(sql, CommandType.Text, timeout);
} /// <summary>
/// The execute scalar.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The <see cref="object"/>.
/// </returns>
public object ExecuteScalar(string sql)
{
return this.ExecuteScalar(sql, CommandType.Text, );
} #endregion #region ExecuteNonQuery /// <summary>
/// The execute non query.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public virtual int ExecuteNonQuery(string sql, CommandType type, int timeout)
{
if (string.IsNullOrWhiteSpace(sql))
{
throw new ArgumentNullException("sql");
}
this.Command.CommandText = sql;
this.Command.CommandType = type;
this.Command.CommandTimeout = timeout;
return this.Command.ExecuteNonQuery();
} /// <summary>
/// The execute non query.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="type">
/// The type.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int ExecuteNonQuery(string sql, CommandType type)
{
return this.ExecuteNonQuery(sql, type, );
} /// <summary>
/// The execute non query.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <param name="timeout">
/// The timeout.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int ExecuteNonQuery(string sql, int timeout)
{
return this.ExecuteNonQuery(sql, CommandType.Text, timeout);
} /// <summary>
/// The execute non query.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int ExecuteNonQuery(string sql)
{
return this.ExecuteNonQuery(sql, CommandType.Text, );
} #endregion /// <summary>
/// The dispose.
/// </summary>
public void Dispose()
{
if (this.Command != null)
{
this.Command.Dispose();
this.Command = null;
} if (this.Connection == null)
{
return;
} if (this.Connection.State == ConnectionState.Open)
{
this.Connection.Close();
} this.Connection.Dispose();
this.Connection = null;
}
}
8.Sql仓储基类
主要是加载有子对象的跟定义实体对象接口
/// <summary>
/// The sql repository base.
/// </summary>
/// <typeparam name="TKey">
/// </typeparam>
/// <typeparam name="TValue">
/// </typeparam>
public abstract class SqlRepositoryBase<TKey, TValue> : SqlCeRepositoryBase<TKey, TValue>
where TValue : EntityBase<TKey>
{
/// <summary>
/// 有子对象的回调委托
/// </summary>
/// <param name="entityAggregate">实体聚合根</param>
/// <param name="childEntityKeyValue">子实体键</param>
public delegate void AppendChildData(TValue entityAggregate, object childEntityKeyValue); /// <summary>
/// 实体工厂
/// </summary>
private readonly IEntityFactory<TValue> m_entityFactory; /// <summary>
/// 子对象集
/// </summary>
private readonly Dictionary<string, AppendChildData> m_childCallbacks; /// <summary>
/// The m_child key datas.
/// </summary>
private readonly Dictionary<string, object> m_childKeyDatas; /// <summary>
/// Initializes a new instance of the <see cref="SqlRepositoryBase{TKey,TValue}"/> class.
/// </summary>
/// <param name="connectionSetting">
/// The connection setting.
/// </param>
protected SqlRepositoryBase(string connectionSetting)
: base(connectionSetting)
{
} /// <summary>
/// Initializes a new instance of the <see cref="SqlRepositoryBase{TKey,TValue}"/> class.
/// </summary>
/// <param name="unitOfWork">
/// The unit of work.
/// </param>
/// <param name="connectionSetting">
/// The connection setting.
/// </param>
protected SqlRepositoryBase(IUnitOfWork unitOfWork, string connectionSetting)
: base(unitOfWork, connectionSetting)
{
this.m_entityFactory = this.BuildEntityFactory();
this.m_childCallbacks = new Dictionary<string, AppendChildData>();
this.m_childKeyDatas = new Dictionary<string, object>();
this.BuildChildCallbacks(this.m_childCallbacks);
} /// <summary>
/// 改为由子类创建实体,不使用工厂
/// </summary>
/// <returns>TValue</returns>
protected abstract IEntityFactory<TValue> BuildEntityFactory(); /// <summary>
/// 创建子对象回调
/// </summary>
/// <param name="childCallbacks">子对象集</param>
protected abstract void BuildChildCallbacks(Dictionary<string, AppendChildData> childCallbacks); /// <summary>
/// 子对象回调集
/// </summary>
protected Dictionary<string, AppendChildData> ChildCallbacks
{
get
{
return this.m_childCallbacks;
}
} /// <summary>
/// The build entity from reader.
/// </summary>
/// <param name="reader">
/// The reader.
/// </param>
/// <returns>
/// The <see cref="TValue"/>.
/// </returns>
protected virtual TValue BuildEntityFromReader(IDataReader reader)
{
TValue entity = this.m_entityFactory.BuildEntity(reader);
if (this.m_childCallbacks != null && this.m_childCallbacks.Count > )
{
DataTable columnData = reader.GetSchemaTable();
foreach (string childKeyName in this.m_childCallbacks.Keys)
{
object childKeyValue;
////判断 DataReader 的数据集合中是否存在一个特定的列名(或字段名)
if (columnData != null && columnData.Rows.Cast<DataRow>().Any(row => row["ColumnName"].ToString() == childKeyName))
{
childKeyValue = reader[childKeyName];
}
else
{
childKeyValue = null;
}
if (m_childKeyDatas.ContainsKey(childKeyName))
{
m_childKeyDatas[childKeyName] = childKeyValue;
}
else
{
m_childKeyDatas.Add(childKeyName, childKeyValue);
}
}
}
return entity;
} /// <summary>
/// The build entity from sql.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The <see cref="TValue"/>.
/// </returns>
protected virtual TValue BuildEntityFromSql(string sql)
{
TValue entity = default(TValue);
using (IDataReader reader = this.ExecuteReader(sql))
{
if (reader.Read())
{
entity = this.BuildEntityFromReader(reader);
}
}
if (entity != null)
{
this.InvokeChildCallbacks(entity);
}
return entity;
} /// <summary>
/// The build entities from sql.
/// </summary>
/// <param name="sql">
/// The sql.
/// </param>
/// <returns>
/// The
/// </returns>
protected virtual List<TValue> BuildEntitiesFromSql(string sql)
{
List<TValue> entities = new List<TValue>();
using (IDataReader reader = this.ExecuteReader(sql))
{
while (reader.Read())
{
entities.Add(this.BuildEntityFromReader(reader));
}
}
return entities;
} /// <summary>
/// The invoke child callbacks.
/// </summary>
/// <param name="entity">
/// The entity.
/// </param>
private void InvokeChildCallbacks(TValue entity)
{
if (this.m_childCallbacks != null && this.m_childCallbacks.Any())
{
foreach (string childKeyName in this.m_childKeyDatas.Keys)
{
object childKeyValue;
this.m_childKeyDatas.TryGetValue(childKeyName, out childKeyValue);
this.m_childCallbacks[childKeyName](entity, childKeyValue);
}
}
}
}
领域层
1.分析业务需求,
假如我现在需要做一个平台的虚拟支付的功能(类似支付宝支付)
业务功能分析
1.开户功能
2.支付功能
3.转账功能
4.冻结解冻功能
2.结构设计
账户类图 Account账户类 Encrypted密保类 密保类又分手机邮箱。 账户类是继承EntityBase类的这样就抽象出他是一个聚合边界,从而对他抽象出仓储(仓储说白了就是保存到数据库的行为)
下面贴出仓储实现类的类图 ,AccountRepository 仓储类继承SqlRepositoryBase 并且继承仓储接口IAccountRepository 看他的方法主要实现了SqlRepositoryBase 的一些抽象方法而
仓储接口则是为空接口,这样做是为了方便后面扩展。
3.分析Account的职责
账户类型的一些基本职责跟行为
账户的职责就是拥有用户的基本新跟账户金额等等一些属性。
账户的基本行为:
1.登录
登录的时候需要验证账户状态是否可登录,等等一些信息
public void Login(string accountName, string loginPassWord)
{
this.ValidateEmpty();
this.ValidateLoginStatus();
if (this.AccountName != accountName)
{
throw new ArgumentException("抱歉!账户名错误");
} if (this.LoginPassWord != loginPassWord)
{
throw new ArgumentException("抱歉!账户登录密码错误");
}
}
2.存款
存款其实也很简单只需要往账户余额上加钱就完事了
public void Deposit(decimal depositAmount)
{
this.Balance += depositAmount;
this.AvailableBalance += depositAmount;
}
3.取款
取款跟存款就相反了但是要注意一点就是需要验证余额是否充足
public void Withdraw(decimal withdrawAmout)
{
this.ValudateAvailableBalance(withdrawAmout);
this.Balance -= withdrawAmout;
this.AvailableBalance -= withdrawAmout;
}
5.冻结
冻结也跟取款类似
public void Freeze(decimal freezeAmount)
{
this.ValudateAvailableBalance(freezeAmount);
this.FreezeAmount += freezeAmount;
this.AvailableBalance -= freezeAmount;
}
6.解冻
解冻就跟冻结相反需要注意的就是验证可解冻金额范围
public void UnFreeze(decimal unFreezeAmount)
{
this.ValudateFreezeAmount(unFreezeAmount);
this.FreezeAmount -= unFreezeAmount;
this.AvailableBalance += unFreezeAmount;
}
7.修改交易密码
public void UpdatePayPassWord(string payPassWord)
{
this.PayPassWord = payPassWord;
}
8.修改登录密码
public void UpdateLoginPassWord(string loginPassWord)
{
this.LoginPassWord = loginPassWord;
}
4.AccountRepository实现的基本方法
主要是实现Account实体对象的 增删查改4个基本方法另外还有2个多的方法
BuildEntityFactory:用来创建读取IDataReader对象的接口
该方法返回一个IEntityFactory<Account>类型
这里只需要新建一个类AccountFactory实现IEntityFactory<out, T>接口即可
/// <summary>
/// The account factory.
/// </summary>
public class AccountFactory : IEntityFactory<Account>
{
/// <summary>
/// 把IDataReader对象解析成Account对象
/// </summary>
/// <param name="reader">
/// The reader.
/// </param>
/// <returns>
/// The <see cref="Account"/>.
/// </returns>
public Account BuildEntity(IDataReader reader)
{
Account account = new Account(Guid.Parse(reader[FieldNames.AccountID].ToString()));
return account;
} /// <summary>
/// 把DataSet对象解析成Account对象
/// </summary>
/// <param name="table">
/// The table.
/// </param>
/// <returns>
/// The <see cref="Account"/>.
/// </returns>
public Account BuildEntity(DataSet table)
{
throw new NotImplementedException();
} /// <summary>
/// 映射数据库字段名称
/// </summary>
public static class FieldNames
{
/// <summary>
/// 表名 Account
/// </summary>
public const string Account = "Account"; /// <summary>
/// 主键 AccountID
/// </summary>
public const string AccountID = "AccountID";
}
}
protected override IEntityFactory<Account> BuildEntityFactory()
{
return new AccountFactory();
}
BuildChildCallbacks:有来加载Acount的外键实体对象
因为Account对象的属性Encrypted密保对象数据保存在其他的表所以这里就把他当成子对象来加载
/// <summary>
/// The build child callbacks.
/// </summary>
/// <param name="childCallbacks">
/// The child callbacks.
/// </param>
protected override void BuildChildCallbacks(Dictionary<string, AppendChildData> childCallbacks)
{
// 主要实现加载密保的方法
childCallbacks.Add(
AccountFactory.FieldNames.AccountID,
(a, b) =>
{
// 此处调用加载密保的方法
a.Encrypted = null;
});
}
下面贴出增删查改的方法 只实现了增加的方法做为实例
/// <summary>
/// 查询
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <returns>
/// The <see cref="Account"/>.
/// </returns>
public override Account FindBy(Guid key)
{
throw new NotImplementedException();
} /// <summary>
/// 增加
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public override void PersistNewItem(IEntity item)
{
// 因为Account是继承自IEntity接口这里直接把item转化成Account类型即可
Account account = (Account)item;
StringBuilder sql = new StringBuilder();
sql.AppendFormat(" INSERT INTO {0}", AccountFactory.FieldNames.Account);
sql.Append(" ( ");
sql.AppendFormat("{0}", AccountFactory.FieldNames.AccountID);
sql.Append(" ) VALUES ( ");
sql.AppendFormat("@{0}", AccountFactory.FieldNames.AccountID);
sql.Append(");"); // 调用父亲类方法实现参数化添加参数
// 调用支持先清除一下这是必须的
this.ClearParameters();
this.AddParameter("@" + AccountFactory.FieldNames.AccountID, account.Key); // 执行sql语句
this.ExecuteNonQuery(sql.ToString());
} /// <summary>
/// 修改
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public override void PersistUpdatedItem(IEntity item)
{
throw new NotImplementedException();
} /// <summary>
/// 删除
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public override void PersistDeletedItem(IEntity item)
{
throw new NotImplementedException();
}
5.现在来实现开户功能
领域结构大致已经说完 现在来说最关心的业务了 第一个业务就是开户了
现在我们需要一个服务类AccountService(这个类的方法基本为静态方法 主要是把各个领域的领域逻辑组织起来现成连贯的业务逻辑)
现在先定义一个方法签名 public static void Register(AccountDTO accountDTO)开户的方法签名
AccountDTO 这个参数实际就是一个传输对象单独封装在一个程序集里面
他的属性主要就是开户的时候录入用户填写的一些信息
/// <summary>
/// 账户传输对象
/// </summary>
public class AccountDTO
{
/// <summary>
/// 账户名
/// </summary>
public string AccountName { get; set; } /// <summary>
/// 昵称
/// </summary>
public string Nickname { get; set; } /// <summary>
/// 登录密码
/// </summary>
public string LoginPassWord { get; set; } /// <summary>
/// 交易密码
/// </summary>
public string PayPassWord { get; set; } /// <summary>
/// 邮箱
/// </summary>
public string Emial { get; set; } /// <summary>
/// 手机
/// </summary>
public string Phone { get; set; } /// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
下一下步就是要把AccountDTO传输对象转化成Account领域对象
转化的时候我们就需要一个工厂类来创建
/// <summary>
/// Builder
/// </summary>
public class Builder
{
/// <summary>
/// 创建Account
/// </summary>
/// <param name="accountDTO">accountDTO</param>
/// <returns>Account</returns>
internal static Account BuilderAccount(AccountDTO accountDTO)
{
Account account = new Account
{
AccountStatus = AccountStatus.Normal,
Balance = 0M,
AvailableBalance = 0M,
FreezeAmount = 0M,
Encrypted = null,
AccountName = accountDTO.AccountName,
Nickname = accountDTO.Nickname,
LoginPassWord = accountDTO.LoginPassWord,
PayPassWord = accountDTO.PayPassWord,
Remark = accountDTO.Remark
}; return account;
}
}
拿到Account对对象因为没有什么逻辑 就直接持久化到数据库了
这时候只需要通过IAccountRepository的Add方法把数据插入到数据库即可
IAccountRepository接口我们通过工厂方法创建
/// <summary>
/// The create i account repository.
/// </summary>
/// <returns>
/// The <see cref="IAccountRepository"/>.
/// </returns>
internal static IAccountRepository CreateIAccountRepository()
{
return new AccountRepository(ConnectionString.Account);
}
/// <summary>
/// The connection string.
/// </summary>
public static class ConnectionString
{
/// <summary>
/// The account.
/// </summary>
public const string Account = "Account";
}
最后贴出开户的全部代码
public static void Register(AccountDTO accountDTO)
{
Account.Account account = Account.Builder.BuilderAccount(accountDTO);
using (IAccountRepository accountRepository = Account.Factory.AccountFactory.CreateIAccountRepository())
{
accountRepository.Add(account);
}
}
6.既然用了账户当然我们就可以支付了
现在开始来实现支付,其实支付就是一种交易 既然交易肯定有买家,卖家这时候就抽象一个交易类来咯
Tread(交易类)
/// <summary>
/// 交易
/// </summary>
public class Trade : EntityBase<Guid>
{
/// <summary>
/// Initializes a new instance of the <see cref="Trade"/> class.
/// </summary>
public Trade()
: base(Guid.NewGuid())
{
} /// <summary>
/// Initializes a new instance of the <see cref="Trade"/> class.
/// </summary>
/// <param name="accountID">
/// The account id.
/// </param>
public Trade(Guid accountID)
: base(accountID)
{
} /// <summary>
/// 买家
/// </summary>
public Account Buyer { get; set; } /// <summary>
/// 卖家
/// </summary>
public Account Seller { get; set; }
}
现在我们需要的是支付交易只需要再新建一个支付类就可以了
这个类主要包括支付的一些信息
/// <summary>
/// 支付
/// </summary>
public class PayOrder : Trade
{
/// <summary>
/// 支付状态
/// </summary>
public PayStatus PayStatus { get; set; } /// <summary>
/// 支付金额
/// </summary>
public decimal PayAmount { get; set; } /// <summary>
/// 支付订单号
/// </summary>
public string PayOrderNumber { get; set; } /// <summary>
/// 创建时间
/// </summary>
public DateTime CreateDateTime { get; set; } /// <summary>
/// 支付时间
/// </summary>
public DateTime? PayDateTime { get; set; } /// <summary>
/// 备注
/// </summary>
public string Remark { get; set; } /// <summary>
/// 支付
/// </summary>
/// <returns>生成账单</returns>
public List<Bill> Pay()
{
AccountService.AccountTransfer(this.Buyer, this.Seller, this.PayAmount);
return null;
}
}
需要注意的是这个类里面包括了一个支付的方法 这个方法返回支付的账单信息集合
/// <summary>
/// 账单
/// </summary>
public class Bill : EntityBase<Guid>
{
/// <summary>
/// 交易金额
/// </summary>
public decimal TradeAmount { get; set; } /// <summary>
/// 账单流水号
/// </summary>
public string OrderNumber { get; set; } /// <summary>
/// 交易类型
/// </summary>
public string TraderType { get; set; } /// <summary>
/// 交易备注
/// </summary>
public string TradeRemark { get; set; } /// <summary>
/// 交易时间
/// </summary>
public DateTime TradeDateTime { get; set; } /// <summary>
/// 交易号
/// </summary>
public string TradeOrderNumber { get; set; } /// <summary>
/// 交易账户
/// </summary>
public Account Account { get; set; } /// <summary>
/// 交易对方账户
/// </summary>
public Account ToAccount { get; set; }
}
这样一来只需要把业务组织起来就可以完成支付了
还是跟开户一样先给支付服务的方法签名 public static void Pay(PayDTO payDTO)
PayDTO传输对象没有什么好说的
/// <summary>
/// 支付传送对象
/// </summary>
public class PayDTO
{
/// <summary>
/// 支付账号
/// </summary>
public string PayAccountNO { get; set; } /// <summary>
/// 支付金额
/// </summary>
public decimal PayAmount { get; set; } /// <summary>
/// 交易密码
/// </summary>
public string PayPassWord { get; set; } /// <summary>
/// 交易备注
/// </summary>
public string Remark { get; set; }
}
1.验证支付信息就没有什么好说的了
2.根据支付账号加载支付账户
3.根据配置加载收款账户
4.创建支付订单
5.调用支付方法并且生成账单
/// <summary>
/// 支付
/// </summary>
/// <param name="payDTO">支付信息</param>
public static void Pay(PayDTO payDTO)
{
// 1 校验基本信息
payDTO.ValidatePayDTO(); // 2 加载账户
// 支付账户
Account.Account payAccount = QueryByAccountName(payDTO.PayAccountNO); // 收款账户
Account.Account sellerAccount = QueryBySeller(); // 3 校验交易密码
payAccount.ValidatePayPassWord(payDTO.PayPassWord); // 4 创建支付订单
PayOrder payOrder = Builder.BuilderPayOrder(payAccount, sellerAccount, payDTO); // 同步处理为支付成功
payOrder.PaySucess(); // 5 调用支付方法
List<Bill> bills = payOrder.Pay(); // 6 持久化数据(开启工作单元;开启数据库事务)
using (IUnitOfWork unitOfWork = Account.Factory.AccountFactory.CreateIUnitOfWork())
{
// 更新账户余额
IAccountRepository accountRepository = Account.Factory.AccountFactory.CreateIAccountRepository(unitOfWork);
accountRepository.Update(payAccount);
accountRepository.Update(sellerAccount); // 生成订单
IPayOrderRepository payOrderRepository = Account.Factory.AccountFactory.CreateIPayOrderRepository(unitOfWork);
payOrderRepository.Add(payOrder); // 生成账单
IBillRepository billRepository = Account.Factory.AccountFactory.CreateIBillRepository(unitOfWork);
billRepository.Add(bills); // 提交工作单元(提交事务,失败自动回滚)
unitOfWork.Complete();
}
}
7.转账
转账也是一种交易跟支付差不多 转账也需要继承至Trade交易类,因为转账属性正向业务,所以抽象一层ForwardTrade正向交易类
/// <summary>
/// 正向交易
/// </summary>
public class ForwardTrade : Trade
{
/// <summary>
/// Initializes a new instance of the <see cref="ForwardTrade"/> class.
/// </summary>
public ForwardTrade()
{
this.TradeOrderNumber = Builder.BuilderOrderNumber();
} /// <summary>
/// Initializes a new instance of the <see cref="ForwardTrade"/> class.
/// </summary>
/// <param name="tradeID">
/// The pay order id.
/// </param>
public ForwardTrade(Guid tradeID)
: base(tradeID)
{
} /// <summary>
/// 交易金额
/// </summary>
public decimal TradeAmount { get; set; } /// <summary>
/// 交易订单号
/// </summary>
public string TradeOrderNumber { get; set; } /// <summary>
/// 创建时间
/// </summary>
public DateTime CreateDateTime { get; set; } /// <summary>
/// 支付时间
/// </summary>
public DateTime? TradeDateTime { get; set; } /// <summary>
/// 备注
/// </summary>
public string Remark { get; set; } /// <summary>
/// 更新交易时间
/// </summary>
/// <param name="tradeDateTime">交易时间</param>
public void UpdateTradeDateTime(DateTime tradeDateTime)
{
this.TradeDateTime = tradeDateTime;
} /// <summary>
/// 交易
/// </summary>
/// <returns>生成账单</returns>
public List<TradeBill> Trade()
{
AccountService.AccountTransfer(this.Buyer, this.Seller, this.TradeAmount);
List<TradeBill> result = new List<TradeBill>
{
this.CreateBill(this.Buyer, this.Seller, TraderType.Out),
this.CreateBill(this.Seller, this.Buyer, TraderType.In)
};
return result;
} /// <summary>
/// 创建账单
/// </summary>
/// <param name="account">交易账户</param>
/// <param name="toAccount">交易对方账户</param>
/// <param name="traderType">交易类型</param>
/// <returns></returns>
private TradeBill CreateBill(Account account, Account toAccount, TraderType traderType)
{
return new TradeBill
{
Account = account,
ToAccount = toAccount,
TraderType = traderType,
TradeRemark = this.Remark,
TradeAmount = this.TradeAmount,
TradeDateTime = DateTime.Now,
TradeOrderNumber = this.TradeOrderNumber,
Balance = account.Balance,
AvailableBalance = account.AvailableBalance,
FreezeAmount = account.FreezeAmount,
BillType = BillType.Pay
};
}
}
转账类只需要继承正向交易类就行了 转账只有一个转账的核心方法
/// <summary>
/// Transfer
/// </summary>
public class TransferOrder : ForwardTrade
{
/// <summary>
/// Initializes a new instance of the <see cref="TransferOrder"/> class.
/// </summary>
public TransferOrder()
{
} /// <summary>
/// Initializes a new instance of the <see cref="TransferOrder"/> class.
/// </summary>
/// <param name="transferID">
/// The pay order id.
/// </param>
public TransferOrder(Guid transferID)
: base(transferID)
{
} /// <summary>
/// 状态
/// </summary>
public TransferStatus TransferStatus { get; set; } /// <summary>
/// 支付成功处理
/// </summary>
public void TransferSucess()
{
if (this.TransferStatus == TransferStatus.Successful)
{
throw new ArgumentException("抱歉!订单已经交易成功");
} this.UpdateTradeDateTime(DateTime.Now);
this.UpdatePayStatus(TransferStatus.Successful);
} /// <summary>
/// 转账
/// </summary>
/// <returns>账单</returns>
public List<TradeBill> Transfer()
{
return this.Trade();
} /// <summary>
/// 修改订单状态
/// </summary>
/// <param name="transferStatus">订单状态</param>
public void UpdatePayStatus(TransferStatus transferStatus)
{
this.TransferStatus = transferStatus;
}
}
最后在服务类组装业务
1 校验基本信息
2 加载 转账账户
3 加载收款账户
4 校验交易密码
5 创建订单
6 调用转账方法
7 持久化数据
/// <summary>
/// 转账
/// </summary>
/// <param name="transferDTO">转账信息</param>
public static void Transfer(TransferDTO transferDTO)
{
// 1 校验基本信息
transferDTO.ValidateTransferDTO(); // 2 加载账户
// 转账账户
Account.Account payAccount = QueryByAccountName(transferDTO.TransferNO); // 收款账户
Account.Account sellerAccount = QueryByAccountName(transferDTO.CollectionNO); // 3 校验交易密码
payAccount.ValidatePayPassWord(transferDTO.PayPassWord); // 4 创建订单
TransferOrder transferOrder = Builder.BuilderTransferOrder(payAccount, sellerAccount, transferDTO); // 同步处理为成功
transferOrder.TransferSucess(); // 5 调用转账方法
List<TradeBill> bills = transferOrder.Transfer(); // 6 持久化数据(开启工作单元;开启数据库事务)
using (IUnitOfWork unitOfWork = Account.Factory.AccountFactory.CreateIUnitOfWork())
{
// 更新账户余额
IAccountRepository accountRepository = Account.Factory.AccountFactory.CreateIAccountRepository(unitOfWork);
accountRepository.Update(payAccount);
accountRepository.Update(sellerAccount); // 生成订单
ITransferOrderRepsoitory transferOrderRepsoitory = Account.Factory.AccountFactory.CreateITransferOrderRepsoitory(unitOfWork);
transferOrderRepsoitory.Add(transferOrder); // 生成账单
IBillRepository billRepository = Account.Factory.AccountFactory.CreateIBillRepository(unitOfWork);
billRepository.Add(bills); // 提交工作单元(提交事务,失败自动回滚)
unitOfWork.Complete();
}
}
8.冻结
冻结主要是将账户的可用余额冻结起来 ,其实就实现一个内部转账就是账户自身AvailableBalance可用余额转到FreezeAmount冻结金额上去 最终生成冻结订单来控制金额变化起到资金控制的作用
这样就可以抽象出一个资金控制类出来
/// <summary>
/// 资金控制
/// </summary>
public class Control : EntityBase<Guid>
{
/// <summary>
/// Initializes a new instance of the <see cref="Control"/> class.
/// </summary>
public Control()
: base(Guid.NewGuid())
{
} /// <summary>
/// Initializes a new instance of the <see cref="Control"/> class.
/// </summary>
/// <param name="accountID">
/// The account id.
/// </param>
public Control(Guid accountID)
: base(accountID)
{
} /// <summary>
/// 控制账户
/// </summary>
public Account Account { get; set; }
}
这样再去设计冻结订单类
/// <summary>
/// 冻结订单
/// </summary>
public class FreezeOrder : Control
{
/// <summary>
/// Initializes a new instance of the <see cref="FreezeOrder"/> class.
/// </summary>
public FreezeOrder()
{
this.FrezeOrderNumber = Builder.BuilderOrderNumber();
} /// <summary>
/// 冻结订单号
/// </summary>
public string FrezeOrderNumber { get; set; } /// <summary>
/// 冻结状态
/// </summary>
public FreezeStatus FreezeStatus { get; set; } /// <summary>
/// 冻结金额
/// </summary>
public decimal FreezeAmount { get; set; } /// <summary>
/// 已解冻金额
/// </summary>
public decimal ThawAmount { get; set; } /// <summary>
/// 备注
/// </summary>
public string FreezeRemark { get; set; } /// <summary>
/// 冻结
/// </summary>
/// <returns>
/// The
/// </returns>
public ControlBill Freeze()
{
this.Account.Freeze(this.FreezeAmount);
this.UpdateFreezeStatus(FreezeStatus.冻结成功);
return this.CreateControlBill();
} /// <summary>
/// 修改冻结订单状态
/// </summary>
/// <param name="freezeStatus">冻结订单状态</param>
public void UpdateFreezeStatus(FreezeStatus freezeStatus)
{
this.FreezeStatus = freezeStatus;
} /// <summary>
/// 解冻
/// </summary>
/// <param name="thawAmount">解冻金额</param>
public void Thaw(decimal thawAmount)
{
if (thawAmount > this.FreezeAmount)
{
throw new AggregateException("抱歉!解冻金额大于冻结金额");
} if (thawAmount > this.FreezeAmount - this.ThawAmount)
{
throw new AggregateException("抱歉!解冻金额过大");
} this.ThawAmount += thawAmount;
} /// <summary>
/// The create control bill.
/// </summary>
/// <returns>
/// The <see cref="ControlBill"/>.
/// </returns>
private ControlBill CreateControlBill()
{
var bill = new ControlBill
{
Account = this.Account,
Balance = this.Account.Balance,
AvailableBalance = this.Account.AvailableBalance,
FreezeAmount = this.Account.FreezeAmount,
ControlAmount = this.FreezeAmount,
ControlDateTime = DateTime.Now,
ControlOrderNumber = this.FrezeOrderNumber,
ControlRemark = this.FreezeRemark,
ControlType = ControlType.Freeze,
OrderNumber = Builder.BuilderOrderNumber()
}; return bill;
}
}
冻结包括2个核心方法冻结跟解冻
服务类只需要组装这些业务冻结功能就完成了
/// <summary>
/// 冻结
/// </summary>
/// <param name="freezeDTO">冻结信息</param>
public static void Freeze(FreezeDTO freezeDTO)
{
freezeDTO.ValidateFreezeDTO();
Account.Account account = QueryByAccountName(freezeDTO.FreezeNO);
FreezeOrder freezeOrder = Builder.BuilderFreezeOrder(account, freezeDTO);
ControlBill controlBill = freezeOrder.Freeze();
using (IUnitOfWork unitOfWork = Account.Factory.AccountFactory.CreateIUnitOfWork())
{
IAccountRepository accountRepository = Account.Factory.AccountFactory.CreateIAccountRepository(unitOfWork);
accountRepository.UpdateFreezeAmount(account); // 生成冻结订单
IFreezeOrderRepository freezeOrderRepository = Account.Factory.AccountFactory.CreateIFreezeOrderRepository(unitOfWork);
freezeOrderRepository.Add(freezeOrder); // 生成冻结账单
IControlBillRepository controlBillRepository = Account.Factory.AccountFactory.CreateIControlBillRepository(unitOfWork);
controlBillRepository.Add(controlBill); unitOfWork.Complete();
}
}
9.解冻
解冻与冻结就相反了
但是也是属于资金控制
只需要设计一个解冻订单类继承资金控制就可以,解冻订单必须包含一个属性是冻结订单
/// <summary>
/// 解冻订单
/// </summary>
public class ThawOrder : Control
{
/// <summary>
/// Initializes a new instance of the <see cref="ThawOrder"/> class.
/// </summary>
/// <param name="freezeOrder">
/// The freeze order.
/// </param>
public ThawOrder(FreezeOrder freezeOrder)
{
this.Initialization(freezeOrder);
} /// <summary>
/// Initializes a new instance of the <see cref="ThawOrder"/> class.
/// </summary>
/// <param name="thawId">
/// The thaw id.
/// </param>
/// <param name="freezeOrder">
/// The freeze order.
/// </param>
public ThawOrder(Guid thawId, FreezeOrder freezeOrder)
: base(thawId)
{
this.Initialization(freezeOrder);
} /// <summary>
/// The initialization.
/// </summary>
/// <param name="freezeOrder">
/// The freeze order.
/// </param>
private void Initialization(FreezeOrder freezeOrder)
{
this.FreezeOrder = freezeOrder;
this.Account = freezeOrder.Account;
} /// <summary>
/// 冻结订单
/// </summary>
public FreezeOrder FreezeOrder { get; private set; } /// <summary>
/// 冻结订单号
/// </summary>
public string ThawOrderNumber { get; set; } /// <summary>
/// 解冻状态
/// </summary>
public ThawStatus ThawStatus { get; set; } /// <summary>
/// 解冻金额
/// </summary>
public decimal ThawAmount { get; set; } /// <summary>
/// 备注
/// </summary>
public string ThawRemark { get; set; } /// <summary>
/// 修改解冻订单状态
/// </summary>
/// <param name="freezeStatus">冻结订单状态</param>
public void UpdateThawStatus(ThawStatus freezeStatus)
{
this.ThawStatus = freezeStatus;
} /// <summary>
/// 解冻
/// </summary>
/// <returns>账单</returns>
public ControlBill Thaw()
{
this.FreezeOrder.Thaw(this.ThawAmount);
this.Account.Thaw(this.ThawAmount);
this.UpdateThawStatus(ThawStatus.解冻成功);
return this.CreateControlBill();
} /// <summary>
/// The create control bill.
/// </summary>
/// <returns>
/// The <see cref="ControlBill"/>.
/// </returns>
private ControlBill CreateControlBill()
{
var bill = new ControlBill
{
Account = this.Account,
Balance = this.Account.Balance,
AvailableBalance = this.Account.AvailableBalance,
FreezeAmount = this.Account.FreezeAmount,
ControlAmount = this.ThawAmount,
ControlDateTime = DateTime.Now,
ControlOrderNumber = this.ThawOrderNumber,
ControlRemark = this.ThawRemark,
ControlType = ControlType.Thaw,
OrderNumber = Builder.BuilderOrderNumber()
}; return bill;
}
}
这样设计出来就简单明了了
/// <summary>
/// 解冻
/// </summary>
/// <param name="thawDTO">解冻信息</param>
public static void Thaw(ThawDTO thawDTO)
{
thawDTO.ValidateThawDTO();
FreezeOrder freezeOrder = QueryFreezeOrder(thawDTO.FreezeOrderNumber);
ThawOrder thawOrder = Builder.BuilderThawOrder(freezeOrder, thawDTO);
ControlBill controlBill = thawOrder.Thaw();
using (IUnitOfWork unitOfWork = Account.Factory.AccountFactory.CreateIUnitOfWork())
{
IAccountRepository accountRepository = Account.Factory.AccountFactory.CreateIAccountRepository(unitOfWork);
accountRepository.UpdateFreezeAmount(thawOrder.Account); // 生成解冻顶
IThawOrderRepsoitory thawOrderRepsoitory = Account.Factory.AccountFactory.CreateIThawOrderRepsoitory(unitOfWork);
thawOrderRepsoitory.Add(thawOrder); // 修改冻结订单
IFreezeOrderRepository freezeOrderRepository = Account.Factory.AccountFactory.CreateIFreezeOrderRepository(unitOfWork);
freezeOrderRepository.Update(freezeOrder); // 生成解冻账单
IControlBillRepository controlBillRepository = Account.Factory.AccountFactory.CreateIControlBillRepository(unitOfWork);
controlBillRepository.Add(controlBill); unitOfWork.Complete();
}
}
到此为止 本篇文章也就结束了 。
本文的目的做到了 业务与持久化无关(数据库操作), 做到了 CodeFirst(代码优先)
最后要做的就是根据领域对象去设计数据库就行
c#领域驱动设计的更多相关文章
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
- DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对?
写在前面 阅读目录: 具体业务场景 业务需求变化 "愚蠢"的应对 消息列表实现 消息详情页实现 消息发送.回复.销毁等实现 回到原点的一些思考 业务需求变化,领域模型变化了吗? 对 ...
- DDD 领域驱动设计-商品建模之路
最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(3)
上一篇:<DDD 领域驱动设计-谈谈 Repository.IUnitOfWork 和 IDbContext 的实践(2)> 这篇文章主要是对 DDD.Sample 框架增加 Transa ...
- DDD 领域驱动设计-两个实体的碰撞火花
上一篇:<DDD 领域驱动设计-领域模型中的用户设计?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 在 ...
- 初探领域驱动设计(2)Repository在DDD中的应用
概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体.值类型和领域服务,也稍微讲到了DDD中的分层结构.但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的, ...
- [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店
一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这 ...
- 领域驱动设计实战—基于DDDLite的权限管理OpenAuth.net
在园子里面,搜索一下“权限管理”至少能得到上千条的有效记录.记得刚开始工作的时候,写个通用的权限系统一直是自己的一个梦想.中间因为工作忙(其实就是懒!)等原因,被无限期搁置了.最近想想,自己写东西时, ...
- 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践
写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...
- 一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?
写在前面 阅读目录: 问题根源是什么? <领域驱动设计-软件核心复杂性应对之道>分层概念 Repository(仓储)职责所在? Domain Model(领域模型)重新设计 Domain ...
随机推荐
- Oracle中exp导出与imp导入的参数(full,owner/formuser/touser)测试
1.exp导出的参数(FULL,OWNER)测试 先知道的一点是full不能与owner共存,还有都是以用户的方式导出(在这里),其中不仅仅包括表,这可能就是下面报warnings的原因,因为Orac ...
- 接口自动化框架(java)--5.通过testng.xml生成extentreport测试报告
这套框架的报告是自己封装的 由于之前已经通过Extentreport插件实现了Testng的IReport接口,所以在testng.xml中使用listener标签并指向实现IReport接口的那个类 ...
- 框架的一些bug问题记录
关于java后台生成的mis表,在添加的add页面和edit页面中无法显示,去控制台里面查看.会有些错误的信息,是由于,你的那些html的标签不正常.导致无法显示. 里面有一个高级搜索,可以对这个选择 ...
- Leetcode: Find Permutation(Unsolve lock problem)
By now, you are given a secret signature consisting of character 'D' and 'I'. 'D' represents a decre ...
- DLG消息
WM_GETMINMAXINFO 0X0024 WM_NCCREATE 0X0081 WM_NCCALCSIZE 0X0083 WM_CREATE 0X0001 WM_SIZE 0X0005 WM ...
- 区块链名词解析:ICO、IFO、IEO和IMO,分别是什么呢?
区块链名词解析:ICO.IFO.IEO和IMO,分别是什么呢?本部分给出了标准答案,但其相当枯燥乏味,建议快进. ICO(Initial Coin Offering),首次代币发行,指区块链项目首次向 ...
- C#导出文本内容到word文档源码
将做工程过程中较好的代码片段珍藏起来,下面的代码内容是关于C#导出文本内容到word文档的代码,希望能对小伙伴们也有好处.<%@ Page Language="C#" Aut ...
- decimal(19,6)什么意思
decimal(19,6)什么意思 数字长度19位,精确到小数点后6位例如0.123456 mysql中varchar(50)最多能存多少个汉字 首先要确定mysql版本4.0版本以下,varchar ...
- 代码修改WinForm datagridview 样式 及数据绑定
#region 表格设置 /// <summary> /// 调整表格 /// </summary> /// <param name="dataGrid&quo ...
- 【Rice】Cultivar versus Variety
From Cindy Haynes, Department of Horticulture As a horticulturist, it is important that I use the ...