Skip to content

Instantly share code, notes, and snippets.

@CanerPatir
Created December 4, 2019 14:09
Show Gist options
  • Select an option

  • Save CanerPatir/8523f6d287c83304de40472283e3dbb7 to your computer and use it in GitHub Desktop.

Select an option

Save CanerPatir/8523f6d287c83304de40472283e3dbb7 to your computer and use it in GitHub Desktop.
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