Dodoã®ç£æ»ã¯çŽããŒã¹ã§ã¯ãããŸãããç£æ»äººã¯ã¿ãã¬ãããæã£ãŠãããç£æ»äººã¯ãã¹ãŠã®è£œåã«æ³šæããŠã¬ããŒããäœæããŸãããããã2020幎ãŸã§ããããã§ãªã¢ã®æ¹èšã¯æ£ç¢ºã«çŽçã§è¡ãããŠããŸãããããã¯ããã®æ¹ãç°¡åã ã£ãããã§ãããã¡ãããããã¯äžæ£ç¢ºãªããŒã¿ããšã©ãŒãæå€±ã«ã€ãªãããŸããã人ã ã¯ééããç¯ããçŽçã倱ãããããã«å€ãã®ããšããããŸãããã®åé¡ãä¿®æ£ããã¿ãã¬ããã®æ¹æ³ãæ¹åããããšã«ããŸãããå®è£ ã¯DDDã䜿çšããããšã決å®ããŸãããã©ã®ããã«ãããããããç§ãã¡ã¯ããã«ããªãã«è©±ããŸãã
ãŸããã³ã³ããã¹ããçè§£ããããã«ãããžãã¹ããã»ã¹ã«ã€ããŠç°¡åã«èª¬æããŸãã補åã®ãããŒãã£ãŒããšãã®æ¹èšç¹ãæ€èšããŠãããæè¡çãªè©³çްã«ç§»ããŸããããã¯å€ãã®ããšã«ãªãã§ãããã
補åã®ç§»åã®ã¹ããŒã ãšç£æ»ãå¿ èŠãªçç±
ç§ãã¡ã®ãããã¯ãŒã¯ã«ã¯600以äžã®ãããã§ãªã¢ããããŸãïŒãããŠãã®æ°ã¯å¢ãç¶ããã§ãããïŒã補åã®æºåãšè²©å£²ãæå¹æéãŸã§ã®ææã®ååŽããããã§ãŒã³ã®ä»ã®ãããã§ãªã¢ãžã®åææã®ç§»åãŸã§ãããããã«åææã®ç§»åãæ¯æ¥ãããŸãããããã§ãªã¢ã®ãã©ã³ã¹ã«ã¯ã補åã®è£œé ã«å¿ èŠãªçŽ120ã®ã¢ã€ãã ã«å ããŠããããã§ãªã¢ãæž æœã«ä¿ã€ããã®å€ãã®æ¶èåãå®¶åºçšåãååŠè¬åãåžžã«å«ãŸããŠããŸããããã«ã¯ãã©ã®åææãè±å¯ã§ã©ããäžè¶³ããŠããããç¥ãããã«ãäŒèšåŠçããå¿ èŠã§ãã
ãäŒèšããšã¯ããããã§ãªã¢ã§ã®åææã®åããæããŸããçŽæã¯ãã©ã³ã¹ã·ãŒãäžã§ãã©ã¹ã§ãããååŽã¯ãã€ãã¹ã§ããããšãã°ããã¶ã泚æãããšããã£ãã·ã£ãŒã¯æ³šæãåãå ¥ããŠåŠçã®ããã«éä¿¡ããŸãããã®åŸãçå°ãåºããããŒãºãããããœãŒã¹ãããããŒããªã©ã®ææãè©°ããŸãããããã®è£œåã¯ãã¹ãŠçç£ã«å ¥ã-ååŽãããŸãããŸããæå¹æéãçµäºãããšååŽãçºçããå ŽåããããŸãã
çŽåãšååŽã®çµæããå庫æ®é«ãã圢æãããŸããããã¯ãæ å ±ã·ã¹ãã ã®éçšã«åºã¥ããŠããã©ã³ã¹ã·ãŒãã«å«ãŸããåææã®éãåæ ããã¬ããŒãã§ããããããã¹ãŠã決æžãã©ã³ã¹ãã§ãããããããå®éã®äŸ¡å€ãããããŸããã€ãŸããçŸåšå®éã«åšåº«ãããŠããåææã®éã§ãã
æ¹èš
å®éã®å€ãèšç®ããããã«ãããªããžã§ã³ãã䜿çšãããŸãïŒãããã¯ãã€ã³ãã³ããªããšãåŒã°ããŸãïŒã
ç£æ»ã¯ãè³Œå ¥ããåææã®éãæ£ç¢ºã«èšç®ããã®ã«åœ¹ç«ã¡ãŸããè³Œå ¥ãå€ããããšãäœæ¥è³æ¬ãåçµãããéå°ãªè£œåãååŽãããªã¹ã¯ãé«ãŸããæå€±ã«ãã€ãªãããŸããåææã®äœå°ã¯å±éºã§ããã ãã§ãªããäžè¶³ãå±éºã§ããããã«ãããäžéšã®è£œåã®çç£ã忢ããåçãæžå°ããå¯èœæ§ããããŸããç£æ»ã¯ãèšé²ãããæªèšäžã®åææã®æå€±ã«ãã£ãŠäŒæ¥ãã©ãã ãã®å©çã倱ã£ãŠããã®ãã確èªããã³ã¹ãã®åæžã«åãçµãã®ã«åœ¹ç«ã¡ãŸãã
ãªããžã§ã³ã¯ãã¬ããŒãã®äœæãªã©ããããªãåŠçãååã«èæ ®ããŠããŒã¿ãå ±æããŸãã
æ¹èšããã»ã¹ã®åé¡ããŸãã¯å€ãæ¹èšã®ä»çµã¿
æ¹èšã¯éªšã®æããããã»ã¹ã§ããæéãããããåææã®æ®éªžã®ã«ãŠã³ããšä¿®æ£ãä¿ç®¡å Žæããšã®åææã®çµæã®èŠçŽãDodoISæ å ±ã·ã¹ãã ãžã®çµæã®å ¥åãªã©ã®ããã€ãã®æ®µéã§æ§æãããŸãã
以åã¯ãåææã®ãªã¹ããèšèŒããããã³ãšçŽã®ãã©ãŒã ã䜿çšããŠç£æ»ãå®è¡ãããŠããŸãããçµæãæåã§èŠçŽã調æŽãããã³Dodo ISã«è»¢éããå Žåãééããç¯ãå¯èœæ§ããããŸããå®å šç£æ»ã§ã¯ã100çš®é¡ä»¥äžã®åææãã«ãŠã³ããããèšç®èªäœãæ·±å€ãæ©æã«è¡ãããããšãå€ããéäžåãäœäžããå¯èœæ§ããããŸãã
åé¡ã解決ããæ¹æ³
Game of ThreadsããŒã ã¯ããããã§ãªã¢ã§äŒèšãéçºããŠããŸãããããã§ãªã¢ã®ç£æ»ãç°¡çŽ åãããç£æ»äººçšã¿ãã¬ããããšãããããžã§ã¯ããç«ã¡äžããããšã«ããŸãããã¢ã«ãŠã³ãã£ã³ã°ã®äž»èŠã³ã³ããŒãã³ããå®è£ ãããŠããç¬èªã®æ å ±ã·ã¹ãã DodoISã§ãã¹ãŠãè¡ãããšã«ããããããµãŒãããŒãã£ã®ã·ã¹ãã ãšçµ±åããå¿ èŠã¯ãããŸãããããã«ãç§ãã¡ãååšãããã¹ãŠã®åœã¯ã远å ã®çµ±åã«é ŒãããšãªãããŒã«ã䜿çšã§ããããã«ãªããŸãã
ãããžã§ã¯ãã®äœæ¥ãéå§ããåã§ãããç§ãã¡ããŒã ã¯DDDãå®éã«é©çšããããšãã顿ã«ã€ããŠè©±ãåããŸããã幞ããªããšã«ããããžã§ã¯ãã®1ã€ã¯ãã§ã«ãã®ã¢ãããŒããããŸãé©çšããŠããã®ã§ãã芧ããã ããäŸããããŸãããããããã£ãã·ã¥ãã¹ã¯ããããžã§ã¯ãã§ãã
ãã®èšäºã§ã¯ãéçºã§äœ¿çšããæŠè¡çãªDDDãã¿ãŒã³ïŒéçŽãã³ãã³ãããã¡ã€ã³ã€ãã³ããã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ãããã³å¶éä»ãã³ã³ããã¹ãã®çµ±åïŒã«ã€ããŠèª¬æããŸããDDDã®æŠç¥çãã¿ãŒã³ãšåºæ¬ã«ã€ããŠã¯èª¬æããŸããã説æããªããšãèšäºãéåžžã«é·ããªããŸããããã«ã€ããŠã¯ãããã¡ã€ã³ããªãã³ãã¶ã€ã³ã«ã€ããŠ10åã§äœãåŠã¶ããšãã§ããŸããïŒããšããèšäºã§ãã§ã«èª¬æããŸããããã
ãªããžã§ã³ã®æ°ããããŒãžã§ã³
ç£æ»ãéå§ããåã«ãæ£ç¢ºã«äœãæ°ããããç¥ãå¿ èŠããããŸãããã®ããã«ããªããžã§ã³ãã³ãã¬ãŒããå¿ èŠã§ãããããã¯ããªãã£ã¹ãããŒãžã£ãŒãã®åœ¹å²ã«ãã£ãŠæ§æãããŸãããªããžã§ã³ãã³ãã¬ãŒãã¯InventoryTemplateãšã³ãã£ãã£ã§ããæ¬¡ã®ãã£ãŒã«ããå«ãŸããŠããŸãã
- ãã³ãã¬ãŒãèå¥å;
- ãããã§ãªã¢ID;
- ãã³ãã¬ãŒãå;
- æ¹èšã«ããŽãªïŒæ¯æãæ¯é±ãæ¯æ¥ã
- ãŠããã;
- ä¿ç®¡å Žæãšãã®ä¿ç®¡å Žæã®åææ
ãã®ãšã³ãã£ãã£ã«ã¯ãCRUDæ©èœãå®è£ ãããŠããããã詳现ã«ã€ããŠã¯èª¬æããŸããã
ç£æ»äººã¯ããã³ãã¬ãŒãã®ãªã¹ããæã£ãŠãããã圌ã¯éå§ããããšãã§ããç£æ»ããããã¯éåžžããããã§ãªã¢ãéããŠãããšãã«çºçããŸããçŸæç¹ã§ã¯ã泚æã¯ãªããåææãåããŠããŸããããã©ã³ã¹ã«é¢ããããŒã¿ã確å®ã«ååŸã§ããŸãã
ç£æ»ãéå§ãããšãç£æ»äººã¯å·èµåº«ãªã©ã®ãŸãŒã³ãéžæããããã§åææãæ°ããŸããå·èµåº«ã®äžã§ã圌ã¯5ããã¯ã®ããŒãºïŒãããã10 kgïŒãèŠãŠãèšç®æ©ã«10 kg * 5ãå ¥åãã[ããã«å ¥å]ãæŒããŸããæ¬¡ã«ãäžçªäžã®æ£ã«ããã«2ã€ã®ããã¯ãããããšã«æ°ã¥ãã[远å ]ãã¯ãªãã¯ããŸãããã®çµæã圌ã¯2ã€ã®æž¬å®å€ïŒãããã50kgãš20kgïŒãæã£ãŠããŸãã
ã¡ãŒã¿ãªã³ã°ç¹å®ã®å°åã®æ€æ»å®ãå ¥åããåææã®éãåŒã³åºããŸãããå¿ ãããåèšãšã¯éããŸãããæ€æ»å®ã¯ã1ããã°ã©ã ã®2ã€ã®æž¬å®å€ãå ¥åããããšãã1åã®æž¬å®ã§2ããã°ã©ã ãå ¥åããããšãã§ããŸããä»»æã®çµã¿åãããå ¥åã§ããŸããéèŠãªããšã¯ãç£æ»äººèªèº«ãæç¢ºã§ãªããã°ãªããªããšããããšã§ãã
èšç®æ©ã€ã³ã¿ãŒãã§ãŒã¹ã
ãã®ãããç£æ»äººã¯æ®µéçã«1ã2æéã§ãã¹ãŠã®åææãæ€èšããç£æ»ãå®äºããŸãã
ã¢ã¯ã·ã§ã³ã®ã¢ã«ãŽãªãºã ã¯éåžžã«åçŽã§ãã
- ç£æ»äººã¯ç£æ»ãéå§ã§ããŸãã
- ç£æ»äººã¯ãéå§ããããªããžã§ã³ã«æž¬å®å€ã远å ã§ããŸãã
- ç£æ»äººã¯ç£æ»ãå®äºããããšãã§ããŸãã
ã·ã¹ãã ã®ããžãã¹èŠä»¶ã¯ããã®ã¢ã«ãŽãªãºã ãã圢æãããŸãã
ãã¡ã€ã³ã®ã¢ã°ãªã²ãŒããã³ãã³ããã€ãã³ãã®æåã®ããŒãžã§ã³ã®å®è£
ãŸããDDDæŠè¡ãã³ãã¬ãŒãã»ããã«å«ãŸããçšèªãå®çŸ©ããŸãããããã®èšäºã§ã¯ããããåç §ããŸãã
æŠè¡çãªDDDãã³ãã¬ãŒã
Aggregateã¯ããšã³ãã£ãã£ãªããžã§ã¯ããšå€ãªããžã§ã¯ãã®ã¯ã©ã¹ã¿ãŒã§ããã¯ã©ã¹ã¿ãŒå ã®ãªããžã§ã¯ãã¯ãããŒã¿å€æŽã«é¢ããŠã¯åäžã®ãšã³ãã£ãã£ã§ããåéèšã«ã¯ããšã³ãã£ãã£ãšå€ã«ã¢ã¯ã»ã¹ããããã®ã«ãŒãèŠçŽ ããããŸãããŠãããã¯å€§ããããªãããã«èšèšããŠãã ããããããã¯å€§éã®ã¡ã¢ãªãæ¶è²»ãããã©ã³ã¶ã¯ã·ã§ã³ãæ£åžžã«å®äºããå¯èœæ§ãäœããªããŸãã
éçŽå¢çã¯ãåäžã®ãã©ã³ã¶ã¯ã·ã§ã³å ã§äžè²«ããŠããå¿ èŠããããªããžã§ã¯ãã®ã»ããã§ãããã®ã¯ã©ã¹ã¿ãŒå ã®ãã¹ãŠã®äžå€æ¡ä»¶ãç£èŠããå¿ èŠããããŸãã
äžå€æ¡ä»¶ã¯ãççŸããããšã®ãªãããžãã¹ã«ãŒã«ã§ãã
ã³ãã³ããŠãããã«å¯Ÿããäœããã®ã¢ã¯ã·ã§ã³ã§ãããã®ã¢ã¯ã·ã§ã³ã®çµæãšããŠãã¢ã°ãªã²ãŒãã®ç¶æ ã倿Žã§ãã1ã€ä»¥äžã®ãã¡ã€ã³ã€ãã³ããçæã§ããŸãã
ãã¡ã€ã³ã€ãã³ãã¯ãäžè²«æ§ãç¶æããããã«å¿ èŠãªãã¢ã°ãªã²ãŒãã®ç¶æ ã®å€æŽã®éç¥ã§ããéçŽã«ããããã©ã³ã¶ã¯ã·ã§ã³ã®äžè²«æ§ãä¿èšŒãããŸãããã¹ãŠã®ããŒã¿ãä»ããã§å€æŽããå¿ èŠããããŸããçµæãšããŠåŸãããäžè²«æ§ã«ãããé·æçãªäžè²«æ§ãä¿èšŒãããŸããããŒã¿ã¯å€æŽãããŸãããçŸåšã¯å€æŽãããŸããããç¡æéã«å€æŽãããŸãããã®ééã¯ãã¡ãã»ãŒãžãã¥ãŒã®èŒ»èŒ³ããããã®ã¡ãã»ãŒãžãåŠçããããã®å€éšãµãŒãã¹ã®æºåããããã¯ãŒã¯ãªã©ãå€ãã®èŠå ã«ãã£ãŠç°ãªããŸãã
ã«ãŒãèŠçŽ äžæã®ã°ããŒãã«èå¥åãæã€ãšã³ãã£ãã£ã§ããåèŠçŽ ã¯ãéåäœå šäœã®äžã§ã®ã¿ããŒã«ã«IDãæã€ããšãã§ããŸãããããã¯çžäºã«åç §ã§ããã«ãŒãèŠçŽ ã®ã¿ãåç §ã§ããŸãã
ããŒã ãšã€ãã³ã
ããŒã ãšããŠã®ããžãã¹èŠä»¶ã説æããŸããããã³ãã³ãã¯ã説æãã£ãŒã«ããæã€åãªãDTOã§ãã
ã³ãã³ããaddmeasurementãã«ã¯ã次ã®ãã£ãŒã«ãããããŸãã
- 枬å®å€-ç¹å®ã®æž¬å®åäœå
ã®åææã®éãæž¬å®å€ãåé€ãããå Žåã¯nullã«ãªãå¯èœæ§ããããŸãã
- ããŒãžã§ã³-枬å®å€ã¯ç·šéã§ãããããããŒãžã§ã³ãå¿
èŠã§ãã
- åææèå¥å;
- 枬å®åäœïŒkg / gãl / mlãããŒã¹;
- ã¹ãã¬ãŒãžãšãªã¢èå¥åã
ã³ãã³ãã³ãŒãã远å ããæž¬å®
public sealed class AddMeasurementCommand
{
// ctor
public double? Value { get; }
public int Version { get; }
public UUId MaterialTypeId { get; }
public UUId MeasurementId { get; }
public UnitOfMeasure UnitOfMeasure { get; }
public UUId InventoryZoneId { get; }
}
ãããã®ã³ãã³ãã®å®è¡ããçããã€ãã³ããå¿ èŠã§ããã€ãã³ããã€ã³ã¿ãŒãã§ãŒã¹ã§ããŒã¯ã
IPublicInventoryEventãŸããå°æ¥ãå€éšã®æ¶è²»è
ãšçµ±åããããã«å¿
èŠã«ãªããŸãã
ã€ãã³ããã¡ãŒã¿ãªã³ã°ãã®ãã£ãŒã«ãã¯ãã³ãã³ããã¡ãŒã¿ãªã³ã°ã®è¿œå ããšåãã§ãããã€ãã³ããçºçãããŠãããã®èå¥åãšãã®ããŒãžã§ã³ãæ ŒçŽãããç¹ãç°ãªããŸãã
ã€ãã³ãã³ãŒããåçµã
public class MeasurementEvent : IPublicInventoryEvent
{
public UUId MaterialTypeId { get; set; }
public double? Value { get; set; }
public UUId MeasurementId { get; set; }
public int MeasurementVersion { get; set; }
public UUId AggregateId { get; set; }
public int Version { get; set; }
public UnitOfMeasure UnitOfMeasure { get; set; }
public UUId InventoryZoneId { get; set; }
}
ã³ãã³ããšã€ãã³ãã«ã€ããŠèª¬æããããéçŽãå®è£ ã§ããŸã
Inventoryã
InventoryAggregateã®å®è£
UMLéèšå³ã€ã³ãã³ããªã
ã¢ãããŒãã¯æ¬¡ã®ãšããã§ãããªããžã§ã³ã®éå§ã«ããéçŽã®äœæãéå§ã
InventoryããŸãããã®ãããfactoryã¡ãœããã䜿çšCreateããã³ãã³ãã§ãªããžã§ã³ãéå§ãStartInventoryCommandãŸãã
åã³ãã³ãã¯ãéèšã®ç¶æ ã倿Žããã€ãã³ãããªã¹ãã«ä¿åããŸãããªã¹ãã¯
changesãèšé²ã®ããã«ã¹ãã¬ãŒãžã«éä¿¡ãããŸãããŸãããããã®å€æŽã«åºã¥ããŠãå€çåãã®ã€ãã³ããçæãããŸãã
ã¢ã°ãªã²ãŒãã
Inventoryäœæããããšããã®åŸã®ãªã¯ãšã¹ãããšã«ã¢ã°ãªã²ãŒãã埩å
ããŠããã®ç¶æ
ã倿Žã§ããŸãã
changesãŠããããæåŸã«åŸ©å ãããŠããã®å€æŽïŒïŒãä¿åãããŸãã
- ç¶æ
ã¯
Restoreãã¢ã°ãªã²ãŒãã®çŸåšã®ã€ã³ã¹ã¿ã³ã¹ã§ãããŒãžã§ã³ã§ãœãŒãããã以åã®ãã¹ãŠã®ã€ãã³ããåçããã¡ãœããã«ãã£ãŠåŸ©å ãããŸãInventoryã
ããã¯ã
Event SourcingãŠãããå
ã§ã®ã¢ã€ãã¢ã®å®è£
ã§ããEvent Sourcingãªããžããªã®ãã¬ãŒã ã¯ãŒã¯å
ã§ã¢ã€ãã¢ãå®è£
ããæ¹æ³ã«ã€ããŠã¯ãå°ãåŸã§èª¬æããŸããVaughn Vernonã®æ¬ããã®çŽ æŽãããã€ã©ã¹ãããããŸãïŒ
ãŠãããã®ç¶æ ã¯ãçºçããé åºã§ã€ãã³ããé©çšããããšã«ãã£ãŠåŸ©å ãããŸãã
次ã«ãããŒã ã«ãã£ãŠããã€ãã®æž¬å®ãè¡ãã
AddMeasurementCommandãŸããç£æ»ã¯ã³ãã³ãã§çµäºãFinishInventoryCommandãŸããã¢ã°ãªã²ãŒãã¯ãäžå€æ¡ä»¶ã«æºæ ããããã«ã¡ãœããã倿Žããéã«ãã®ç¶æ
ãæ€èšŒããŸãã
ãŠãããã¯
Inventoryå®å
šã«ããŒãžã§ã³ç®¡çãããŠãããåæž¬å®å€ãåæ§ã§ããããšã«æ³šæããããšãéèŠã§ããæž¬å®ã¯ããå°é£ã§ã-ã€ãã³ãåŠçæ¹æ³ã®ç«¶åã解決ããå¿
èŠããããŸãWhen(MeasurementEvent e)ãã³ãŒãã§ã¯ãã³ãã³ãã®åŠçã®ã¿ã瀺ãAddMeasurementCommandãŸãã
éèšã€ã³ãã³ããªã³ãŒã
public sealed class Inventory : IEquatable<Inventory>
{
private readonly List<IInventoryEvent> _changes = new List<IInventoryEvent>();
private readonly List<InventoryMeasurement> _inventoryMeasurements = new List<InventoryMeasurement>();
internal Inventory(UUId id, int version, UUId unitId, UUId inventoryTemplateId,
UUId startedBy, InventoryState state, DateTime startedAtUtc, DateTime? finishedAtUtc)
: this(id)
{
Version = version;
UnitId = unitId;
InventoryTemplateId = inventoryTemplateId;
StartedBy = startedBy;
State = state;
StartedAtUtc = startedAtUtc;
FinishedAtUtc = finishedAtUtc;
}
private Inventory(UUId id)
{
Id = id;
Version = 0;
State = InventoryState.Unknown;
}
public UUId Id { get; private set; }
public int Version { get; private set; }
public UUId UnitId { get; private set; }
public UUId InventoryTemplateId { get; private set; }
public UUId StartedBy { get; private set; }
public InventoryState State { get; private set; }
public DateTime StartedAtUtc { get; private set; }
public DateTime? FinishedAtUtc { get; private set; }
public ReadOnlyCollection<IInventoryEvent> Changes => _changes.AsReadOnly();
public ReadOnlyCollection<InventoryMeasurement> Measurements => _inventoryMeasurements.AsReadOnly();
public static Inventory Restore(UUId inventoryId, IInventoryEvent[] events)
{
var inventory = new Inventory(inventoryId);
inventory.ReplayEvents(events);
return inventory;
}
public static Inventory Restore(UUId id, int version, UUId unitId, UUId inventoryTemplateId,
UUId startedBy, InventoryState state, DateTime startedAtUtc, DateTime? finishedAtUtc,
InventoryMeasurement[] measurements)
{
var inventory = new Inventory(id, version, unitId, inventoryTemplateId,
startedBy, state, startedAtUtc, finishedAtUtc);
inventory._inventoryMeasurements.AddRange(measurements);
return inventory;
}
public static Inventory Create(UUId inventoryId)
{
if (inventoryId == null)
{
throw new ArgumentNullException(nameof(inventoryId));
}
return new Inventory(inventoryId);
}
public void ReplayEvents(params IInventoryEvent[] events)
{
if (events == null)
{
throw new ArgumentNullException(nameof(events));
}
foreach (var @event in events.OrderBy(e => e.Version))
{
Mutate(@event);
}
}
public void AddMeasurement(AddMeasurementCommand command)
{
if (command == null)
{
throw new ArgumentNullException(nameof(command));
}
Apply(new MeasurementEvent
{
AggregateId = Id,
Version = Version + 1,
UnitId = UnitId,
Value = command.Value,
MeasurementVersion = command.Version,
MaterialTypeId = command.MaterialTypeId,
MeasurementId = command.MeasurementId,
UnitOfMeasure = command.UnitOfMeasure,
InventoryZoneId = command.InventoryZoneId
});
}
private void Apply(IInventoryEvent @event)
{
Mutate(@event);
_changes.Add(@event);
}
private void Mutate(IInventoryEvent @event)
{
When((dynamic) @event);
Version = @event.Version;
}
private void When(MeasurementEvent e)
{
var existMeasurement = _inventoryMeasurements.SingleOrDefault(x => x.MeasurementId == e.MeasurementId);
if (existMeasurement is null)
{
_inventoryMeasurements.Add(new InventoryMeasurement
{
Value = e.Value,
MeasurementId = e.MeasurementId,
MeasurementVersion = e.MeasurementVersion,
PreviousValue = e.PreviousValue,
MaterialTypeId = e.MaterialTypeId,
UserId = e.By,
UnitOfMeasure = e.UnitOfMeasure,
InventoryZoneId = e.InventoryZoneId
});
}
else
{
if (!existMeasurement.Value.HasValue)
{
throw new InventoryInvalidStateException("Change removed measurement");
}
if (existMeasurement.MeasurementVersion == e.MeasurementVersion - 1)
{
existMeasurement.Value = e.Value;
existMeasurement.MeasurementVersion = e.MeasurementVersion;
existMeasurement.UnitOfMeasure = e.UnitOfMeasure;
existMeasurement.InventoryZoneId = e.InventoryZoneId;
}
else if (existMeasurement.MeasurementVersion < e.MeasurementVersion)
{
throw new MeasurementConcurrencyException(Id, e.MeasurementId, e.Value);
}
else if (existMeasurement.MeasurementVersion == e.MeasurementVersion &&
existMeasurement.Value != e.Value)
{
throw new MeasurementConcurrencyException(Id, e.MeasurementId, e.Value);
}
else
{
throw new NotChangeException();
}
}
}
// Equals
// GetHashCode
}
ãæž¬å®æžã¿ãã€ãã³ããçºçãããšããã®èå¥åãæã€æ¢åã®æž¬å®å€ã®ååšããã§ãã¯ãããŸããããã§ãªãå Žåã¯ãæ°ããæž¬å®å€ã远å ãããŸãã
ãã®å Žåã远å ã®ãã§ãã¯ãå¿ èŠã§ãã
- ãªã¢ãŒã枬å®ãç·šéããããšã¯ã§ããŸããã
- çä¿¡ããŒãžã§ã³ã¯åã®ããŒãžã§ã³ããã倧ããããå¿
èŠããããŸãã
æ¡ä»¶ãæºãããããšãæ¢åã®æž¬å®å€ã«æ°ããå€ãšæ°ããããŒãžã§ã³ãèšå®ã§ããŸããããŒãžã§ã³ãå°ããå Žåãããã¯ç«¶åã§ãããã®ããã«ãäŸå€ãã¹ããŒããŸã
MeasurementConcurrencyExceptionãããŒãžã§ã³ãäžèŽããå€ãç°ãªãå Žåããããç«¶åç¶æ³ã§ããããŒãžã§ã³ãšå€ã®äž¡æ¹ãäžèŽããå Žåã倿Žã¯çºçããŠããŸããããã®ãããªç¶æ³ã¯éåžžçºçããŸããã
ãæž¬å®ããšã³ãã£ãã£ã«ã¯ããæž¬å®ã®è¿œå ãã³ãã³ããšãŸã£ããåããã£ãŒã«ããå«ãŸããŠããŸãã
ãšã³ãã£ãã£ã³ãŒããããªãŒãºã
public class InventoryMeasurement
{
public UUId MeasurementId { get; set; }
public UUId MaterialTypeId { get; set; }
public UUId UserId { get; set; }
public double? Value { get; set; }
public int MeasurementVersion { get; set; }
public UnitOfMeasure UnitOfMeasure { get; set; }
public UUId InventoryZoneId { get; set; }
}
ãããªãã¯ã¢ã°ãªã²ãŒãã¡ãœããã®äœ¿çšã¯ããŠããããã¹ãã«ãã£ãŠååã«å®èšŒãããŠããŸãã
ãŠããããã¹ãã³ãŒããæ¹èšéå§åŸã«æž¬å®å€ã远å ã
[Fact]
public void WhenAddMeasurementAfterStartInventory_ThenInventoryHaveOneMeasurement()
{
var inventoryId = UUId.NewUUId();
var inventory = Domain.Inventories.Entities.Inventory.Create(inventoryId);
var unitId = UUId.NewUUId();
inventory.StartInventory(Create.StartInventoryCommand()
.WithUnitId(unitId)
.Please());
var materialTypeId = UUId.NewUUId();
var measurementId = UUId.NewUUId();
var measurementVersion = 1;
var value = 500;
var cmd = Create.AddMeasurementCommand()
.WithMaterialTypeId(materialTypeId)
.WithMeasurement(measurementId, measurementVersion)
.WithValue(value)
.Please();
inventory.AddMeasurement(cmd);
inventory.Measurements.Should().BeEquivalentTo(new InventoryMeasurement
{
MaterialTypeId = materialTypeId,
MeasurementId = measurementId,
MeasurementVersion = measurementVersion,
Value = value,
UnitOfMeasure = UnitOfMeasure.Quantity
});
}
ãã¹ãŠããŸãšããïŒã³ãã³ããã€ãã³ããã€ã³ãã³ããªéèš
ã€ã³ãã³ããªã®çµäºãå®è¡ãããšãã®ã€ã³ãã³ããªã®éçŽã©ã€ããµã€ã¯ã«ã
ãã®å³ã¯ãã³ãã³ãåŠçã®ããã»ã¹ã瀺ããŠããŸã
FinishInventoryCommandãåŠçããåInventoryã«ãã³ãã³ãå®è¡æã®ãŠãããã®ç¶æ
ã埩å
ããå¿
èŠããããŸãããããè¡ãã«ã¯ããã®ãŠãããã§å®è¡ããããã¹ãŠã®ã€ãã³ããã¡ã¢ãªã«ããŒãããŠåçããŸãïŒp.1ïŒã
æ¹èšã®å®äºæç¹ã§ããã§ã«æ¬¡ã®ã€ãã³ãããããŸã-æ¹èšã®éå§ãš3ã€ã®æž¬å®å€ã®è¿œå ããããã®ã€ãã³ãã¯ãã³ãã³ãåŠçã®çµæãšããŠç»å Ž
StartInventoryCommandããAddMeasurementCommandããã«å¿ããŠããããŒã¿ããŒã¹ã§ã¯ãããŒãã«ã®åè¡ã«ãã€ãã³ãèªäœã®ãªããžã§ã³IDãããŒãžã§ã³ãããã³æ¬æãå«ãŸããŠããŸãã
ãã®æ®µéã§ãã³ãã³ããå®è¡ããŸã
FinishInventoryCommandïŒp.2ïŒããã®ã³ãã³ãã¯ãæåã«ãŠãããã®çŸåšã®ç¶æ
ã®æå¹æ§ïŒãªããžã§ã³ãç¶æ
ã«ããããšInProgressïŒã確èªããæ¬¡ã«FinishInventoryEventãªã¹ãã«ã€ãã³ãã远å ããããšã«ãã£ãŠæ°ããç¶æ
倿ŽãçæããŸãchangesïŒé
ç®3ïŒã
ã³ãã³ããå®äºãããšããã¹ãŠã®å€æŽãããŒã¿ããŒã¹ã«ä¿åãããŸãããã®çµæãã€ãã³ãã®æ°ããè¡ãš
FinishInventoryEventãŠãããã®ææ°ããŒãžã§ã³ãããŒã¿ããŒã¹ã«è¡šç€ºãããŸãïŒp.4ïŒã
ã¿ã€ã
InventoryïŒãªããžã§ã³ïŒ-ãã¹ãããããšã³ãã£ãã£ã«é¢é£ããéçŽèŠçŽ ãšã«ãŒãèŠçŽ ããããã£ãŠãã¿ã€ãInventoryã¯ãŠãããã®å¢çãå®çŸ©ããŸããéçŽå¢çã«ã¯ãã¿ã€ãMeasurementïŒæž¬å®ïŒã®ãšã³ãã£ãã£ã®ãªã¹ããããã³éçŽã§å®è¡ããããã¹ãŠã®ã€ãã³ãã®ãªã¹ãïŒchangesïŒãå«ãŸããŸãã
æ©èœå šäœã®å®è£
æ©èœãšã¯ãç¹å®ã®ããžãã¹èŠä»¶ã®å®è£ ãæå³ããŸãããã®äŸã§ã¯ã枬å®ã®è¿œå æ©èœã«ã€ããŠæ€èšããŸãããã®æ©èœãå®è£ ããã«ã¯ããã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ãïŒ
ApplicationServiceïŒã®æŠå¿µãçè§£ããå¿
èŠããããŸãã
ã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ã¯ããã¡ã€ã³ã¢ãã«ã®çŽæ¥ã¯ã©ã€ã¢ã³ãã§ãã Application Servicesã¯ãACIDããŒã¿ããŒã¹ã䜿çšãããšãã«ãã©ã³ã¶ã¯ã·ã§ã³ãä¿èšŒããç¶æ é·ç§»ãã¢ãããã¯ã«ä¿æãããããã«ããŸããããã«ãã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ã¯ã»ãã¥ãªãã£äžã®æžå¿µã«ã察åŠããŸãã
ãã§ã«ãŠãããããããŸã
Inventory..ãæ©èœå
šäœãå®è£
ããããã«ãã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ãå®å
šã«äœ¿çšããŸãããã®äžã§ãæ¥ç¶ãããŠãããã¹ãŠã®ãšã³ãã£ãã£ã®ååšãšããŠãŒã¶ãŒã®ã¢ã¯ã»ã¹æš©ã確èªããå¿
èŠããããŸãããã¹ãŠã®æ¡ä»¶ãæºããããåŸã«ã®ã¿ããŠãããã®çŸåšã®ç¶æ
ãä¿åããã€ãã³ããå€éšã«éä¿¡ããããšãã§ããŸããã¢ããªã±ãŒã·ã§ã³ãµãŒãã¹ãå®è£
ããã«ã¯ãã䜿çšããŸãMediatRã
æ©èœã³ãŒããæž¬å®å€ã®è¿œå ã
public class AddMeasurementChangeHandler
: IRequestHandler<AddMeasurementChangeRequest, AddMeasurementChangeResponse>
{
// dependencies
// ctor
public async Task<AddMeasurementChangeResponse> Handle(
AddMeasurementChangeRequest request,
CancellationToken ct)
{
var inventory =
await _inventoryRepository.GetAsync(request.AddMeasurementChange.InventoryId, ct);
if (inventory == null)
{
throw new NotFoundException($"Inventory {request.AddMeasurementChange.InventoryId} is not found");
}
var user = await _usersRepository.GetAsync(request.UserId, ct);
if (user == null)
{
throw new SecurityException();
}
var hasPermissions =
await _authPermissionService.HasPermissionsAsync(request.CountryId, request.Token, inventory.UnitId, ct);
if (!hasPermissions)
{
throw new SecurityException();
}
var unit = await _unitRepository.GetAsync(inventory.UnitId, ct);
if (unit == null)
{
throw new InvalidRequestDataException($"Unit {inventory.UnitId} is not found");
}
var unitOfMeasure =
Enum.Parse<UnitOfMeasure>(request.AddMeasurementChange.MaterialTypeUnitOfMeasure);
var addMeasurementCommand = new AddMeasurementCommand(
request.AddMeasurementChange.Value,
request.AddMeasurementChange.Version,
request.AddMeasurementChange.MaterialTypeId,
request.AddMeasurementChange.Id,
unitOfMeasure,
request.AddMeasurementChange.InventoryZoneId);
inventory.AddMeasurement(addMeasurementCommand);
await HandleAsync(inventory, ct);
return new AddMeasurementChangeResponse(request.AddMeasurementChange.Id, user.Id, user.GetName());
}
private async Task HandleAsync(Domain.Inventories.Entities.Inventory inventory, CancellationToken ct)
{
await _inventoryRepository.AppendEventsAsync(inventory.Changes, ct);
try
{
await _localQueueDataService.Publish(inventory.Changes, ct);
}
catch (Exception ex)
{
_logger.LogError(ex, "error occured while handling action");
}
}
}
ã€ãã³ããœãŒã·ã³ã°
å®è£ äžã«ãããã€ãã®çç±ã§ESã¢ãããŒããéžæããããšã«ããŸããã
- Dodoã«ã¯ããã®ã¢ãããŒãã®æåäŸããããŸãã
- ESã䜿çšãããšãã€ã³ã·ãã³ãäžã®åé¡ãç°¡åã«çè§£ã§ããŸãããã¹ãŠã®ãŠãŒã¶ãŒã¢ã¯ã·ã§ã³ãä¿åãããŸãã
- åŸæ¥ã®ã¢ãããŒããæ¡çšããå ŽåãESã«ç§»è¡ããããšã¯ã§ããŸããã
å®è£ ã®èãæ¹ã¯éåžžã«åçŽã§ã-ã³ãã³ãã®çµæãšããŠè¡šç€ºããããã¹ãŠã®æ°ããã€ãã³ããããŒã¿ããŒã¹ã«è¿œå ããŸããã¢ã°ãªã²ãŒãã埩å ããããã«ããã¹ãŠã®ã€ãã³ããååŸããŠã€ã³ã¹ã¿ã³ã¹ã§åçããŸããæ¯å倧éã®ã€ãã³ããååŸããªãããã«ããããã«ãNã€ãã³ãããšã«ç¶æ ãåé€ãããã®ã¹ãããã·ã§ããã®æ®ããåçããŸãã
Inventory Aggregate Store ID
internal sealed class InventoryRepository : IInventoryRepository
{
// dependencies
// ctor
static InventoryRepository()
{
EventTypes = typeof(IEvent)
.Assembly.GetTypes().Where(x => typeof(IEvent).IsAssignableFrom(x))
.ToDictionary(t => t.FullName, x => x);
}
public async Task AppendAsync(IReadOnlyCollection<IEvent> events, CancellationToken ct)
{
using (var session = await _dbSessionFactory.OpenAsync())
{
if (events.Count == 0) return;
try
{
foreach (var @event in events)
{
await session.ExecuteAsync(Sql.AppendEvent,
new
{
@event.AggregateId,
@event.Version,
@event.UnitId,
Type = @event.GetType().FullName,
Data = JsonConvert.SerializeObject(@event),
CreatedDateTimeUtc = DateTime.UtcNow
}, cancellationToken: ct);
}
}
catch (MySqlException e)
when (e.Number == (int) MySqlErrorCode.DuplicateKeyEntry)
{
throw new OptimisticConcurrencyException(events.First().AggregateId, "");
}
}
}
public async Task<Domain.Models.Inventory> GetInventoryAsync(
UUId inventoryId,
CancellationToken ct)
{
var events = await GetEventsAsync(inventoryId, 0, ct);
if (events.Any()) return Domain.Models.Inventory.Restore(inventoryId, events);
return null;
}
private async Task<IEvent[]> GetEventsAsync(
UUId id,
int snapshotVersion,
CancellationToken ct)
{
using (var session = await _dbSessionFactory.OpenAsync())
{
var snapshot = await GetInventorySnapshotAsync(session, inventoryId, ct);
var version = snapshot?.Version ?? 0;
var events = await GetEventsAsync(session, inventoryId, version, ct);
if (snapshot != null)
{
snapshot.ReplayEvents(events);
return snapshot;
}
if (events.Any())
{
return Domain.Inventories.Entities.Inventory.Restore(inventoryId, events);
}
return null;
}
}
private async Task<Inventory> GetInventorySnapshotAsync(
IDbSession session,
UUId id,
CancellationToken ct)
{
var record =
await session.QueryFirstOrDefaultAsync<InventoryRecord>(Sql.GetSnapshot, new {AggregateId = id},
cancellationToken: ct);
return record == null ? null : Map(record);
}
private async Task<IInventoryEvent[]> GetEventsAsync(
IDbSession session,
UUId id,
int snapshotVersion,
CancellationToken ct)
{
var rows = await session.QueryAsync<EventRecord>(Sql.GetEvents,
new
{
AggregateId = id,
Version = snapshotVersion
}, cancellationToken: ct);
return rows.Select(Map).ToArray();
}
private static IEvent Map(EventRecord e)
{
var type = EventTypes[e.Type];
return (IEvent) JsonConvert.DeserializeObject(e.Data, type);
}
}
internal class EventRecord
{
public string Type { get; set; }
public string Data { get; set; }
}
æ°ãæã®éçšåŸããŠãããã€ã³ã¹ã¿ã³ã¹ã«ãã¹ãŠã®ãŠãŒã¶ãŒã¢ã¯ã·ã§ã³ãä¿åããå¿ èŠããªãããšã«æ°ä»ããŸãããäŒæ¥ã¯ãã®æ å ±ãäžå䜿çšããŸãããããã¯èšã£ãŠãããã®ã¢ãããŒããç¶æããã«ã¯ãªãŒããŒãããããããŸãããã¹ãŠã®é·æãšçæãè©äŸ¡ããåŸãESããåŸæ¥ã®ã¢ãããŒãã«ç§»è¡ããäºå®ã§ããèšå·
EventsãInventoriesãšã«çœ®ãæãMeasurementsãŸãã
å€éšã®å¶éãããã³ã³ããã¹ããšã®çµ±å
ããã¯ãå¶éãããã³ã³ããã¹ã
Inventoryãå€ã®äžçãšçžäºäœçšããæ¹æ³ã§ãã
ãªããžã§ã³ã³ã³ããã¹ããšä»ã®ã³ã³ããã¹ããšã®çžäºäœçšããã®å³ã¯ãã³ã³ããã¹ãããµãŒãã¹ãããã³ããããçžäºã«å±ããŠããããšã瀺ããŠããŸãã
以äžã®å Žå
AuthãInventoryåã³DatacatalogãåãµãŒãã¹ã«å¯ŸããŠ1ã€ã®å¶éãããã³ã³ããã¹ãããããŸããã¢ããªã¹ã¯ããã€ãã®æ©èœãå®è¡ããŸãããçŸåšã¯ãããã§ãªã¢ã®äŒèšæ©èœã«ã®ã¿é¢å¿ããããŸããæ¹èšã«å ããŠãäŒèšã«ã¯ããããã§ãªã¢ã§ã®åææã®ç§»åïŒåé ã転éãååŽïŒãå«ãŸããŸãã
HTTP
ãã®ãµãŒãã¹ã¯HTTP
InventoryãAuthä»ããŠå¯Ÿè©±ããŸãããŸãããŠãŒã¶ãŒã¯ã«çŽé¢ããŸãAuthãããã«ããããŠãŒã¶ãŒã¯äœ¿çšå¯èœãªåœ¹å²ã®1ã€ãéžæããããã«æ±ããããŸãã
- ã·ã¹ãã ã«ã¯ããŠãŒã¶ãŒãç£æ»äžã«éžæãããç£æ»äººãã®åœ¹å²ããããŸãã
- .
- .
æåŸã®æ®µéã§ããŠãŒã¶ãŒã¯ããã®ããŒã¯ã³ãæã£ãŠããŸã
Authããªããžã§ã³ãµãŒãã¹ã¯ãã®ããŒã¯ã³ãæ€èšŒããå¿
èŠãããAuthãããæ€èšŒãèŠæ±ããŸããAuthããŒã¯ã³ã®æå¹æéãåããŠãããã©ãããææè
ã®ãã®ãã©ããããŸãã¯å¿
èŠãªã¢ã¯ã»ã¹æš©ããããã©ããã確èªããŸãããã¹ãŠãé 調ã§ããã°Inventoryãã¹ã¿ã³ããCookieïŒãŠãŒã¶ãŒIDããã°ã€ã³ããããã§ãªã¢IDïŒã«ä¿åããCookieã®æå¹æéãèšå®ããŸãã
泚ã
Authãã®ãµãŒãã¹ãã©ã®ããã«æ©èœãããã«ã€ããŠã¯ããæ¿èªã®åŸ®åŠãïŒOAuth2.0ãã¯ãããžãŒã®æŠèŠãã®èšäºã§è©³ãã説æããŸãããã¡ãã»ãŒãžãã¥ãŒãä»ããŠ
ä»ã®ãµãŒãã¹ãš
Inventory察話ããŸããå瀟ã¯ãRabbitMQãã¡ãã»ãŒãžãããŒã«ãŒãšããŠäœ¿çšãããã®äžã®ãã€ã³ãã£ã³ã°ã§ããMassTransitã䜿çšããŠããŸãã
RMQïŒã€ãã³ãã®æ¶è²»
ãã£ã¬ã¯ããªãµãŒãã¹---äŒèšãåœãéšéããããã§ãªã¢ã®åææãªã©ãå¿ èŠãªãã¹ãŠã®ãšã³ãã£ãã£
DatacatalogãæäŸãInventoryãŸãã
ã€ã³ãã©ã¹ãã©ã¯ãã£ã®è©³çްã«ã¯è§Šããã«ãã€ãã³ããæ¶è²»ããåºæ¬çãªèãæ¹ã«ã€ããŠèª¬æããŸãããã£ã¬ã¯ããªãµãŒãã¹ã®åŽã§ã¯ããã¹ãŠããã§ã«ã€ãã³ããå ¬éããæºåãã§ããŠããŸããåææãšã³ãã£ãã£ã®äŸãèŠãŠã¿ãŸãããã
ããŒã¿ã«ã¿ãã°ã€ãã³ãå¥çŽã³ãŒã
namespace Dodo.DataCatalog.Contracts.Products.v1
{
public class MaterialType
{
public UUId Id { get; set; }
public int Version { get; set; }
public int CountryId { get; set; }
public UUId DepartmentId { get; set; }
public string Name { get; set; }
public MaterialCategory Category { get; set; }
public UnitOfMeasure BasicUnitOfMeasure { get; set; }
public bool IsRemoved { get; set; }
}
public enum UnitOfMeasure
{
Quantity = 1,
Gram = 5,
Milliliter = 7,
Meter = 8,
}
public enum MaterialCategory
{
Ingredient = 1,
SemiFinishedProduct = 2,
FinishedProduct = 3,
Inventory = 4,
Packaging = 5,
Consumables = 6
}
}
ãã®æçš¿ã¯ã«å ¬éãããŠã
exchangeãŸããåãµãŒãã¹ã¯ãexchange-queueã€ãã³ããæ¶è²»ããããã®ç¬èªã®ãã³ãã«ãäœæã§ããŸãã
RMQããªããã£ããä»ããŠã€ãã³ããšãã®æ¶è²»ãå ¬éããããã®ã¹ããŒã ã
æçµçã«ããµãŒãã¹ããµãã¹ã¯ã©ã€ãã§ãããšã³ãã£ãã£ããšã«ãã¥ãŒããããŸããæ®ã£ãŠããã®ã¯ãæ°ããããŒãžã§ã³ãããŒã¿ããŒã¹ã«ä¿åããããšã ãã§ãã
Datacatalogã®ã€ãã³ãã³ã³ã·ã¥ãŒããŒã³ãŒã
public class MaterialTypeConsumer : IConsumer<Dodo.DataCatalog.Contracts.Products.v1.MaterialType>
{
private readonly IMaterialTypeRepository _materialTypeRepository;
public MaterialTypeConsumer(IMaterialTypeRepository materialTypeRepository)
{
_materialTypeRepository = materialTypeRepository;
}
public async Task Consume(ConsumeContext<Dodo.DataCatalog.Contracts.Products.v1.MaterialType> context)
{
var materialType = new AddMaterialType(context.Message.Id,
context.Message.Name,
(int)context.Message.Category,
(int)context.Message.BasicUnitOfMeasure,
context.Message.CountryId,
context.Message.DepartmentId,
context.Message.IsRemoved,
context.Message.Version);
await _materialTypeRepository.SaveAsync(materialType, context.CancellationToken);
}
}
RMQïŒã€ãã³ãã®å ¬é
ã¢ããªã¹ã®ã¢ã«ãŠã³ãã£ã³ã°éšåã¯ã
Inventoryãªããžã§ã³ããŒã¿ãå¿
èŠãšããæ®ãã®æ©èœããµããŒãããããã«ããŒã¿ãæ¶è²»ããŸããä»ã®ãµãŒãã¹ã«éç¥ããããã¹ãŠã®ã€ãã³ãã¯ãã€ã³ã¿ãŒãã§ãŒã¹ã§ããŒã¯ãããŠããŸãIPublicInventoryEventããã®çš®ã®ã€ãã³ããçºçãããšã倿Žãã°ïŒchangesïŒããããããåé¢ãããã£ã¹ããããã¥ãŒã«éä¿¡ããŸãããã®ããã«ã2ã€ã®ããŒãã«ã䜿çšãpublicqueueãpublicqueue_archiveãŸãã
ã¡ãã»ãŒãžã®é ä¿¡ãä¿èšŒããããã«ãéåžžãããŒã«ã«ãã¥ãŒããšåŒã°ãããã¿ãŒã³ã䜿çšããŸããããã¯ãæå³ã
Transactional outbox patternãŸããã¢ã°ãªã²ãŒãã®ç¶æ
ã®ä¿åInventoryãšã€ãã³ãã®ããŒã«ã«ãã¥ãŒãžã®éä¿¡ã¯ã1ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ã§è¡ãããŸãããã©ã³ã¶ã¯ã·ã§ã³ãã³ãããããããšããã«ããããŒã«ãŒã«ã¡ãã»ãŒãžãéä¿¡ããããšããŸãã
ã¡ãã»ãŒãžãéä¿¡ãããå Žåããã¥ãŒããåé€ãããŸã
publicqueueãããã§ãªãå Žåã¯ãåŸã§ã¡ãã»ãŒãžãéä¿¡ããããšããŸããæ¬¡ã«ãã¢ããªã¹ããã³ããŒã¿ãã€ãã©ã€ã³ã®ãµãã¹ã¯ã©ã€ããŒãã¡ãã»ãŒãžãæ¶è²»ããŸãããã®ããŒãã«publicqueue_archiveã«ã¯ãããæç¹ã§å¿
èŠã«ãªã£ãå Žåã«ã€ãã³ããåãã£ã¹ãããããã®ã«äŸ¿å©ãªããŒã¿ãæ°žä¹
ã«ä¿åãããŸãã
ã¡ãã»ãŒãžãããŒã«ãŒã«ã€ãã³ããå
¬éããããã®ã³ãŒã
internal sealed class BusDataService : IBusDataService
{
private readonly IPublisherControl _publisherControl;
private readonly IPublicQueueRepository _repository;
private readonly EventMapper _eventMapper;
public BusDataService(
IPublicQueueRepository repository,
IPublisherControl publisherControl,
EventMapper eventMapper)
{
_repository = repository;
_publisherControl = publisherControl;
_eventMapper = eventMapper;
}
public async Task ConsumePublicQueueAsync(int batchEventSize, CancellationToken cancellationToken)
{
var events = await _repository.GetAsync(batchEventSize, cancellationToken);
await Publish(events, cancellationToken);
}
public async Task Publish(IEnumerable<IPublicInventoryEvent> events, CancellationToken ct)
{
foreach (var @event in events)
{
var publicQueueEvent = _eventMapper.Map((dynamic) @event);
await _publisherControl.Publish(publicQueueEvent, ct);
await _repository.DeleteAsync(@event, ct);
}
}
}
ã¬ããŒãã®ããã«ã€ãã³ããã¢ããªã¹ã«éä¿¡ããŸããæå€±ãšäœå°ã®ã¬ããŒãã䜿çšãããšãä»»æã®2ã€ã®ãªããžã§ã³ãçžäºã«æ¯èŒã§ããŸãããŸãããã§ã«è¿°ã¹ãéèŠãªã¬ããŒããå庫æ®é«ãããããŸãã
ãªãããŒã¿ãã€ãã©ã€ã³ã«ã€ãã³ããéä¿¡ããã®ã§ããïŒãã¹ãŠåã-ã¬ããŒãçšã§ãããæ°ããã¬ãŒã«ã§ã®ã¿ã§ãã以åã¯ããã¹ãŠã®ã¬ããŒããã¢ããªã¹ã«ååšããŠããŸããããçŸåšã¯åé€ãããŠããŸããããã«ã¯ãçç£ããŒã¿ãšåæããŒã¿ã®ä¿åãšåŠçãšãã2ã€ã®è²¬ä»»ããããŸããOLTPãšOLAPã§ããããã¯ãã€ã³ãã©ã¹ãã©ã¯ãã£ãšéçºã®äž¡æ¹ã®èгç¹ããéèŠã§ãã
çµè«
ãã¡ã€ã³ããªãã³ãã¶ã€ã³ã®ååãšå®è·µã«åŸãããšã«ããããŠãŒã¶ãŒã®ããžãã¹ããŒãºãæºããä¿¡é Œæ§ãé«ãæè»ãªã·ã¹ãã ãæ§ç¯ããããšãã§ããŸããããŸãšããªè£œåã ãã§ãªãã倿Žãç°¡åãªåªããã³ãŒããå ¥æã§ããŸãããããªãã®ãããžã§ã¯ãã«ãã¡ã€ã³ããªãã³ãã¶ã€ã³ã䜿çšããå Žæãããããšãé¡ã£ãŠããŸãã
DDDã®è©³çްã«ã€ããŠã¯ãDDDevotionã³ãã¥ããã£ããã³DDDevotionYoutubeãã£ã³ãã«ãã芧ãã ãããDodoEngineeringãã£ããã®Telegramã§èšäºã«ã€ããŠè©±ãåãããšãã§ããŸãã