Work in progress for the Ordering microservice Domain Model
This commit is contained in:
parent
b6befd623a
commit
9aed8221f2
11
src/Services/Ordering/Ordering.API/DTO/OrderDTO.cs
Normal file
11
src/Services/Ordering/Ordering.API/DTO/OrderDTO.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
|
||||||
|
{
|
||||||
|
public class OrderDTO
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
26
src/Services/Ordering/Ordering.API/DTO/OrderItemDTO.cs
Normal file
26
src/Services/Ordering/Ordering.API/DTO/OrderItemDTO.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public sealed class OrderItemDTO
|
||||||
|
{
|
||||||
|
public OrderItemDTO() { }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public int FulfillmentRemaining { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.ItemId, this.Quantity, this.FulfillmentRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||||
|
{
|
||||||
|
public class Buyer : AggregateRoot
|
||||||
|
{
|
||||||
|
public Buyer(Guid buyerId, string name, string lastName, string email, Address address, string phoneNumber)
|
||||||
|
{
|
||||||
|
this.BuyerId = buyerId;
|
||||||
|
this.Name = name;
|
||||||
|
this.LastName = lastName;
|
||||||
|
this.Email = email;
|
||||||
|
this.Address = address;
|
||||||
|
this.PhoneNumber = phoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Buyer() { } // infrastructure
|
||||||
|
|
||||||
|
public virtual Guid BuyerId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string Name
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string LastName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string Email
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Address Address
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string PhoneNumber
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||||
|
{
|
||||||
|
public class Address : ValueObject<Address> //A VO doesn't have IDENTITY, like in this case, the Address
|
||||||
|
{
|
||||||
|
public virtual String Street
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String City
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String State
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String StateCode
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String Country
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String CountryCode
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual String ZipCode
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual double Latitude
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual double Longitude
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||||
|
{
|
||||||
|
public class Order : AggregateRoot
|
||||||
|
{
|
||||||
|
public Order(Guid buyerId, Address shippingAddress, Address billingAddress)
|
||||||
|
: this(buyerId, shippingAddress, billingAddress, DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Order(Guid buyerId, Address shippingAddress, Address billingAddress, DateTime orderDate)
|
||||||
|
{
|
||||||
|
this.BuyerId = buyerId;
|
||||||
|
this.ShippingAddress = shippingAddress;
|
||||||
|
this.BillingAddress = billingAddress;
|
||||||
|
this.OrderDate = orderDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Order() { } // Infrastructure. EF might need a plain constructor. Do not use.
|
||||||
|
|
||||||
|
//Order ID comes derived from the Entity base class
|
||||||
|
|
||||||
|
List<OrderItem> _orderItems;
|
||||||
|
public virtual List<OrderItem> orderItems
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_orderItems == null)
|
||||||
|
_orderItems = new List<OrderItem>();
|
||||||
|
|
||||||
|
return _orderItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_orderItems = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Guid BuyerId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Address ShippingAddress
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Address BillingAddress
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual DateTime OrderDate
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual OrderStatus Status
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||||
|
{
|
||||||
|
public class OrderItem : Entity
|
||||||
|
{
|
||||||
|
//(CDLTLL) Might remove this constructor so
|
||||||
|
// just with a method in the Order-AggregateRoot you can add an OrderItem..
|
||||||
|
//public OrderItem(Guid itemId, decimal itemPrice, int quantity)
|
||||||
|
//{
|
||||||
|
// this.Id = itemId;
|
||||||
|
// this.ItemPrice = itemPrice;
|
||||||
|
// this.Quantity = quantity;
|
||||||
|
// this.FulfillmentRemaining = quantity; <---- Put this logic into the AggregateRoot method AddOrderItem()
|
||||||
|
//}
|
||||||
|
|
||||||
|
protected OrderItem() { } // Infrastructure. EF might need a plain constructor. Do not use.
|
||||||
|
|
||||||
|
public Guid ItemId { get; set; }
|
||||||
|
|
||||||
|
public decimal ItemPrice { get; set; }
|
||||||
|
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
|
public int FulfillmentRemaining { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.ItemId, this.Quantity, this.FulfillmentRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||||
|
{
|
||||||
|
public enum OrderStatus
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
New,
|
||||||
|
Submitted,
|
||||||
|
InProcess,
|
||||||
|
Backordered,
|
||||||
|
Shipped,
|
||||||
|
Canceled,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
|
||||||
|
{
|
||||||
|
interface IBuyerRepository
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||||
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
|
||||||
|
{
|
||||||
|
public interface IOrderRepository : IRepository<Order>
|
||||||
|
{
|
||||||
|
Order Get(int associatedConferenceId);
|
||||||
|
|
||||||
|
void ModifyOrder(Order seatOrder);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
{
|
{
|
||||||
public class AggregateRoot
|
public class AggregateRoot : Entity
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,130 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
{
|
{
|
||||||
public class Entity
|
/// <summary>
|
||||||
|
/// Base class for entities
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Entity
|
||||||
{
|
{
|
||||||
|
//Members
|
||||||
|
|
||||||
|
int? _requestedHashCode;
|
||||||
|
Guid _Id;
|
||||||
|
|
||||||
|
|
||||||
|
//Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the persisten object identifier
|
||||||
|
/// </summary>
|
||||||
|
public virtual Guid Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _Id;
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Public Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if this entity is transient, ie, without identity at this moment
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if entity is transient, else false</returns>
|
||||||
|
public bool IsTransient()
|
||||||
|
{
|
||||||
|
return this.Id == Guid.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate identity for this entity
|
||||||
|
/// </summary>
|
||||||
|
public void GenerateNewIdentity()
|
||||||
|
{
|
||||||
|
if ( IsTransient())
|
||||||
|
this.Id = IdentityGenerator.NewSequentialGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change current identity for a new non transient identity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity">the new identity</param>
|
||||||
|
public void ChangeCurrentIdentity(Guid identity)
|
||||||
|
{
|
||||||
|
if ( identity != Guid.Empty)
|
||||||
|
this.Id = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Overrides Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="M:System.Object.Equals"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"><see cref="M:System.Object.Equals"/></param>
|
||||||
|
/// <returns><see cref="M:System.Object.Equals"/></returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null || !(obj is Entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Object.ReferenceEquals(this, obj))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Entity item = (Entity)obj;
|
||||||
|
|
||||||
|
if (item.IsTransient() || this.IsTransient())
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return item.Id == this.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="M:System.Object.GetHashCode"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="M:System.Object.GetHashCode"/></returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
if (!IsTransient())
|
||||||
|
{
|
||||||
|
if (!_requestedHashCode.HasValue)
|
||||||
|
_requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
|
||||||
|
|
||||||
|
return _requestedHashCode.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return base.GetHashCode();
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// equals operator
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">left parameter</param>
|
||||||
|
/// <param name="right">right parameter</param>
|
||||||
|
/// <returns>true if equals</returns>
|
||||||
|
public static bool operator ==(Entity left, Entity right)
|
||||||
|
{
|
||||||
|
if (Object.Equals(left, null))
|
||||||
|
return (Object.Equals(right, null)) ? true : false;
|
||||||
|
else
|
||||||
|
return left.Equals(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Distinct operator
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">left parameter</param>
|
||||||
|
/// <param name="right">right parameter</param>
|
||||||
|
/// <returns>True if different</returns>
|
||||||
|
public static bool operator !=(Entity left, Entity right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base interface for implement a "Repository Pattern", for
|
||||||
|
/// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
|
||||||
|
/// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Indeed, DbSet is already a generic repository and therefore you would not need a Repo.
|
||||||
|
/// But using this Repo interface allows us to ensure PI (Persistence Ignorant) principle
|
||||||
|
/// within our domain model
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="TEntity">Type of entity for this repository </typeparam>
|
||||||
|
public interface IRepository<TEntity> : IDisposable
|
||||||
|
where TEntity : Entity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the unit of work in this repository
|
||||||
|
/// </summary>
|
||||||
|
IUnitOfWork UnitOfWork { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add item into repository
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">Item to add to repository</param>
|
||||||
|
void Add(TEntity item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete item
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">Item to delete</param>
|
||||||
|
void Remove(TEntity item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set item as modified
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">Item to modify</param>
|
||||||
|
void Modify(TEntity item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///Track entity into this repository, really in UnitOfWork.
|
||||||
|
///In EF this can be done with Attach and with Update in NH
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">Item to attach</param>
|
||||||
|
void TrackItem(TEntity item);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets modified entity into the repository.
|
||||||
|
/// When calling Commit() method in UnitOfWork
|
||||||
|
/// these changes will be saved into the storage
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="persisted">The persisted item</param>
|
||||||
|
/// <param name="current">The current item</param>
|
||||||
|
void Merge(TEntity persisted, TEntity current);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get element by entity key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Entity key value</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
TEntity Get(Guid id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all elements of type TEntity in repository
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of selected elements</returns>
|
||||||
|
IEnumerable<TEntity> GetAll();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all elements of type TEntity in repository
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pageIndex">Page index</param>
|
||||||
|
/// <param name="pageCount">Number of elements in each page</param>
|
||||||
|
/// <param name="orderByExpression">Order by expression for this query</param>
|
||||||
|
/// <param name="ascending">Specify if order is ascending</param>
|
||||||
|
/// <returns>List of selected elements</returns>
|
||||||
|
IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get elements of type TEntity in repository
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filter">Filter that each element do match</param>
|
||||||
|
/// <returns>List of selected elements</returns>
|
||||||
|
IEnumerable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contract for ‘UnitOfWork pattern’. For more
|
||||||
|
/// related info see http://martinfowler.com/eaaCatalog/unitOfWork.html or
|
||||||
|
/// http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
|
||||||
|
/// In this Microservice, the Unit Of Work is implemented using the out-of-box
|
||||||
|
/// Entity Framework Context (EF Core DbContext) persistence engine. But in order to
|
||||||
|
/// comply the PI (Persistence Ignorant) principle in our Domain, we implement this interface/contract.
|
||||||
|
/// This interface/contract should be complied by any UoW implementation to be used with this Domain.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUnitOfWork
|
||||||
|
: IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Commit all changes made in a container.
|
||||||
|
/// </summary>
|
||||||
|
///<remarks>
|
||||||
|
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||||
|
/// then an exception is thrown
|
||||||
|
///</remarks>
|
||||||
|
void Commit();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Commit all changes made in a container.
|
||||||
|
/// </summary>
|
||||||
|
///<remarks>
|
||||||
|
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||||
|
/// then 'client changes' are refreshed - Client wins
|
||||||
|
///</remarks>
|
||||||
|
void CommitAndRefreshChanges();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rollback tracked changes. See references of UnitOfWork pattern
|
||||||
|
/// </summary>
|
||||||
|
void RollbackChanges();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
|
{
|
||||||
|
internal static class IdentityGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This algorithm generates secuential GUIDs across system boundaries, ideal for databases
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Guid NewSequentialGuid()
|
||||||
|
{
|
||||||
|
byte[] uid = Guid.NewGuid().ToByteArray();
|
||||||
|
byte[] binDate = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
|
||||||
|
|
||||||
|
byte[] secuentialGuid = new byte[uid.Length];
|
||||||
|
|
||||||
|
secuentialGuid[0] = uid[0];
|
||||||
|
secuentialGuid[1] = uid[1];
|
||||||
|
secuentialGuid[2] = uid[2];
|
||||||
|
secuentialGuid[3] = uid[3];
|
||||||
|
secuentialGuid[4] = uid[4];
|
||||||
|
secuentialGuid[5] = uid[5];
|
||||||
|
secuentialGuid[6] = uid[6];
|
||||||
|
// set the first part of the 8th byte to '1100' so
|
||||||
|
// later we'll be able to validate it was generated by us
|
||||||
|
|
||||||
|
secuentialGuid[7] = (byte)(0xc0 | (0xf & uid[7]));
|
||||||
|
|
||||||
|
// the last 8 bytes are sequential,
|
||||||
|
// it minimizes index fragmentation
|
||||||
|
// to a degree as long as there are not a large
|
||||||
|
// number of Secuential-Guids generated per millisecond
|
||||||
|
|
||||||
|
secuentialGuid[9] = binDate[0];
|
||||||
|
secuentialGuid[8] = binDate[1];
|
||||||
|
secuentialGuid[15] = binDate[2];
|
||||||
|
secuentialGuid[14] = binDate[3];
|
||||||
|
secuentialGuid[13] = binDate[4];
|
||||||
|
secuentialGuid[12] = binDate[5];
|
||||||
|
secuentialGuid[11] = binDate[6];
|
||||||
|
secuentialGuid[10] = binDate[7];
|
||||||
|
|
||||||
|
return new Guid(secuentialGuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
145
src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs
Normal file
145
src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for value objects in domain.
|
||||||
|
/// Value
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValueObject">The type of this value object</typeparam>
|
||||||
|
public class ValueObject<TValueObject> : IEquatable<TValueObject>
|
||||||
|
where TValueObject : ValueObject<TValueObject>
|
||||||
|
{
|
||||||
|
|
||||||
|
//IEquatable and Override Equals operators
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="M:System.Object.IEquatable{TValueObject}"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other"><see cref="M:System.Object.IEquatable{TValueObject}"/></param>
|
||||||
|
/// <returns><see cref="M:System.Object.IEquatable{TValueObject}"/></returns>
|
||||||
|
public bool Equals(TValueObject other)
|
||||||
|
{
|
||||||
|
if ((object)other == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Object.ReferenceEquals(this, other))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//compare all public properties
|
||||||
|
PropertyInfo[] publicProperties = this.GetType().GetProperties();
|
||||||
|
|
||||||
|
if ((object)publicProperties != null
|
||||||
|
&&
|
||||||
|
publicProperties.Any())
|
||||||
|
{
|
||||||
|
return publicProperties.All(p =>
|
||||||
|
{
|
||||||
|
var left = p.GetValue(this, null);
|
||||||
|
var right = p.GetValue(other, null);
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof(TValueObject).IsAssignableFrom(left.GetType()))
|
||||||
|
{
|
||||||
|
//check not self-references...
|
||||||
|
return Object.ReferenceEquals(left, right);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return left.Equals(right);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="M:System.Object.Equals"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"><see cref="M:System.Object.Equals"/></param>
|
||||||
|
/// <returns><see cref="M:System.Object.Equals"/></returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if ((object)obj == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Object.ReferenceEquals(this, obj))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ValueObject<TValueObject> item = obj as ValueObject<TValueObject>;
|
||||||
|
|
||||||
|
if ((object)item != null)
|
||||||
|
return Equals((TValueObject)item);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="M:System.Object.GetHashCode"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="M:System.Object.GetHashCode"/></returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hashCode = 31;
|
||||||
|
bool changeMultiplier = false;
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
//compare all public properties
|
||||||
|
PropertyInfo[] publicProperties = this.GetType().GetProperties();
|
||||||
|
|
||||||
|
|
||||||
|
if ((object)publicProperties != null
|
||||||
|
&&
|
||||||
|
publicProperties.Any())
|
||||||
|
{
|
||||||
|
foreach (var item in publicProperties)
|
||||||
|
{
|
||||||
|
object value = item.GetValue(this, null);
|
||||||
|
|
||||||
|
if ((object)value != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
hashCode = hashCode * ((changeMultiplier) ? 59 : 114) + value.GetHashCode();
|
||||||
|
|
||||||
|
changeMultiplier = !changeMultiplier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hashCode = hashCode ^ (index * 13);//only for support {"a",null,null,"a"} <> {null,"a","a",null}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left"></param>
|
||||||
|
/// <param name="right"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool operator ==(ValueObject<TValueObject> left, ValueObject<TValueObject> right)
|
||||||
|
{
|
||||||
|
if (Object.Equals(left, null))
|
||||||
|
return (Object.Equals(right, null)) ? true : false;
|
||||||
|
else
|
||||||
|
return left.Equals(right);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left"></param>
|
||||||
|
/// <param name="right"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool operator !=(ValueObject<TValueObject> left, ValueObject<TValueObject> right)
|
||||||
|
{
|
||||||
|
return !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0-*",
|
"version": "1.0.0-*",
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NETStandard.Library": "1.6.0"
|
"NETStandard.Library": "1.6.0",
|
||||||
|
"System.Reflection.TypeExtensions": "4.1.0",
|
||||||
|
"System.Runtime.Serialization.Primitives": "4.1.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user