ChuanGoing 2019-11-11
Part distance of nearly two months, on the one hand because the other things came up when, on the other hand is the lack of preparation before, there are several places on the field did not want to drive transparent, there is no continuation code word. Currently the network includes most of the garden Domain Driven Design article about storekeeper layer are not described in detail, but simply in passing: the drive does not care about the specific areas of persistent how landing. However, as a "human ape" is not open around the inevitable persistence. Benpian will briefly introduce the use of this lightweight ORM Dapper how to implement persistence.
Benpian learning curve:
1. Domain Model
2. Storage areas
3. Simple service implementation
Domain Model Design
I use a common business model to introduce electric business domain model design, due to limited space, here introduces orders and order details model, based on the following business:
1. generate the order number when creating an order to calculate the total price of goods while generating the order details.
2. Obtain order information according to the order number
My case essentially covers several major areas of the model portfolio: field - entity - the value of the object, these basic concepts is not elaborated here, found the garden a lot.
public partial class Order : DomainEntity<Guid> { /// <summary> /// 订单流水号 /// </summary> public string Sn { get; private set; } /// <summary> /// 总价 /// </summary> public decimal TotalPrice { get; private set; } /// <summary> /// 状态 /// </summary> public OrderStatus Status { get; Private SET ;} /// <Summary> /// payment time /// </ Summary> public Long PaymentTime { GET ; Private SET ;} /// <Summary> /// expiration time /// </ Summary > public Long ExpireTime { GET ; Private SET ;} /// <Summary> /// Note /// </ Summary> public String the Description { GET ;private set; } /// <summary> /// 用户 /// </summary> public Guid UserId { get; private set; } public string Adress { get; private set; } /// <summary> /// 订单明细 /// </summary> [Ignore] public List<OrderItem> OrderItems { get; private set; } }
public partial class OrderItem : Entity<Guid> { /// <summary> /// 订单编号 /// </summary> public Guid OrderId { get; private set; } /// <summary> /// 商品编号 /// </summary> public Guid ProductId { get; private set; } /// <summary> /// 商品单价 /// </summary> public decimal Price { get; private set; } /// <summary> /// 数量 /// </summary> public int Count { get; private set; } /// <summary> /// 加入时间 /// </summary> public long JoinTime { get; private set; } }
Order class can be seen as DomainEntity (Field Entity - aggregate root), OrderItem of the Entity (entity), Order, and a service aggregation OrderItem composition. Why do you divide it? There are two reasons:
1. The present embodiment does not involve complicated operations, there is no direct service operation for the Order Details
2. Order Details depend on the order, the order life cycle along with the body produce and fade away
Orders and Order Details are designed to be "partial", because so far, our entity class or POCO, also known as anemia model. Therefore, in order to impart vitality model, we need to add some behavior:
public partial class Order { public void Add(Guid userId, Adress adress, string description, List<OrderItem> orderItems) { Sn = Guid.NewGuid().ToString("N"); TotalPrice = orderItems.Sum(i => i.Price * i.Count); Status = OrderStatus.TobePaid; ExpireTime = DateTimeOffset.Now.AddMinutes(-30).ToUnixTimeMilliseconds(); UserId = userId; Adress = adress.ToString(); Description = description; orderItems.ForEach(i => { i.SetOrder(this); }); SetItems(orderItems); } public void Pay() { Status = OrderStatus.Paid; PaymentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); } public void SetItems(List<OrderItem> orderItems) { OrderItems = orderItems; } }
In this way, we are given a certain behavior to the field.
Field warehousing
In conjunction with Part I Preliminary Asp.net Core Series --2.ORM: Dapper achieve MySql databases of various types of operating warehousing examples presented here as a separate domain model design "field storage layer"
public class OrderRepository : DapperRepository<Order, Guid>, IOrderRepository { public OrderRepository(IComponentContext container, IDapperDbContext dbContext) : base(container, dbContext) { } public Order GetBySn(string sn) { var order = QuerySingleOrDefault<Order>("SELECT * FROM `Order` WHERE `Sn`=@Sn;", new { Sn = sn }); if (order != null) { order.SetItems(Query<OrderItem>("SELECT * FROM `OrderItem` WHERE `OrderId`=@OrderId;", new { OrderId = order.Id }).ToList()); } return order; } }
Field warehousing implements the interface defined in the Domain
public interface IOrderRepository : IRepository<Order, Guid>, IScopeInstance { Order GetBySn(string sn); }
When Notably, this field storage layer plays a role of connecting the isolation areas directly dependent layer for persistence.
Simple service implementation
Next, we realize how persistent data storage business in the field of call service layer. New Application project:
Orders defined interface services
public interface IOrderService : IScopeInstance { void Add(OrderViewModel order); OrderViewResult Get(string sn); }
Orders service implementation
public void Add(OrderViewModel view) { if (view == null) new Exception("参数无效"); var order = new Order(); Mapper.Map(view, order); order.Add(view.UserId, view.Adress, view.Description, order.OrderItems); _repo.Insert(order); order.OrderItems.ForEach(i => _itemRepo.Insert(i)); } public OrderViewResult Get(string sn) { var order = _repo.GetBySn(sn); OrderViewResult result = new OrderViewResult (); return order == null ? zero : Mapper.Map (order, result); }
In Webapi controller project folder under the new OrderController
public class OrderController : Controller { private readonly IOrderService _service; public OrderController(IOrderService service) { _service = service; } [HttpPost] public void Add([FromBody]OrderViewModel order) { _service.Add(order); } [HttpGet] public OrderViewResult Get([FromQuery]string sn) { return _service.Get(sn); } }
See yesterday's specific code source code links
In this way, we achieve a persistent domain model.
review
Recall In this part, were introduced: the domain model, the field of warehousing, simple service and then use postman simulate http request demonstrates to create and access data.
Benpian simply describes the services sector and related concepts, followed by the opportunity to do a detailed discussion, will introduce the next log module and permissions module.
Code
Benpian Github source involved in the https://github.com/ChuanGoing/Start.git can be found in Domain branch.