Created
December 4, 2019 14:09
-
-
Save CanerPatir/8523f6d287c83304de40472283e3dbb7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace StockDomain | |
| { | |
| class Stock // aggregate root = nosql doc optimistic lock etc. | |
| { | |
| /* | |
| * | |
| * noSql document like this | |
| * { | |
| * quantity, | |
| * listingId, | |
| * storeId?, | |
| * WarehouseId?, | |
| * Reservations: [ | |
| * {id, quantity, type, timestamp} | |
| * ] | |
| * } | |
| */ | |
| public string Id { get; } | |
| public int Version { get; set; } // consistency optimistic locking | |
| public int Quantity { get; private set; } | |
| public int ListingId { get; } | |
| public int? StoreId { get; } | |
| public int? WarehouseId { get; } | |
| public List<StockReservation> Reservations { get; } = new List<StockReservation>(); | |
| public int CurrentQuantity => Quantity - Reservations.Sum(x => x.Quantity); // just calculated value | |
| public Stock(int quantity, int listingId, int? storeId, int? warehouseId) | |
| { | |
| if (storeId == null && warehouseId == null) | |
| { | |
| throw new BusinessException("Stock must belong to either store or warehouse"); | |
| } | |
| if (storeId != null && warehouseId != null) | |
| { | |
| throw new BusinessException("Stock must belong to either store or warehouse"); | |
| } | |
| Quantity = quantity; | |
| ListingId = listingId; | |
| StoreId = storeId; | |
| WarehouseId = warehouseId; | |
| Id = GenerateId($"{listingId}_{storeId ?? warehouseId}"); | |
| } | |
| private static string GenerateId(string concat) | |
| { | |
| Encoding encoding = new UTF8Encoding(); | |
| byte[] input = encoding.GetBytes(concat); | |
| using (var stream = new MemoryStream(input)) | |
| { | |
| return MurMurHash3.Hash(stream); | |
| } | |
| } | |
| public void Reserve(int quantity, int type) | |
| { | |
| // check business rules | |
| var reservation = new StockReservation(quantity, type); | |
| Reservations.Add(reservation); | |
| RaiseDomainEvent(new Reserved(reservation.Id)); | |
| } | |
| public void ReleaseReservation(Guid reservationId) | |
| { | |
| // check business rules | |
| Reservations.RemoveAll(s => s.Id == reservationId); | |
| RaiseDomainEvent(new ReservationReleased(reservation.Id)); | |
| } | |
| public void ReleaseOutOfDateReservations(Guid reservationId) | |
| { | |
| // check business rules | |
| // release timed out reservations | |
| foreach (var reservation in releasedREservations) | |
| { | |
| RaiseDomainEvent(new ReservationReleased(reservation.Id)); | |
| } | |
| } | |
| public void CompleteReservation(List<Guid> reservationIds) | |
| { | |
| // check business rules | |
| Quantity -= Reservations.Where(r => reservationIds.Any(rId => rId == r.Id)).Sum(r => r.Quantity) | |
| reservationIds.ForEach(reservationId => ReleaseReservation(reservationId)); | |
| } | |
| } | |
| public class StockReservation // Reservation value object | |
| { | |
| public Guid Id { get; } = new Guid(); | |
| public int Quantity { get; } | |
| public int Type { get; } | |
| public long Timestamp { get; } | |
| public StockReservation(int quantity, int type) | |
| { | |
| Quantity = quantity; | |
| Type = type; | |
| Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment