@reventlessdev/online-shop-hybrid-ordering 1.0.0-alpha.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +904 -0
  2. package/LICENSE +202 -0
  3. package/__mocks__/emptyModule.js +1 -0
  4. package/package.json +57 -0
  5. package/rescript.json +37 -0
  6. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res +29 -0
  7. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct.res.mjs +71 -0
  8. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res +27 -0
  9. package/src/CatalogProduct/StateChangeSlice/SyncCatalogProduct_Behavior.res.mjs +73 -0
  10. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res +15 -0
  11. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts.res.mjs +69 -0
  12. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res +8 -0
  13. package/src/CatalogProduct/StateViewSliceStream/AvailableProducts_Projection.res.mjs +38 -0
  14. package/src/Customer/Aggregate/Customer.res +24 -0
  15. package/src/Customer/Aggregate/Customer.res.mjs +64 -0
  16. package/src/Customer/Aggregate/Customer_Behavior.res +41 -0
  17. package/src/Customer/Aggregate/Customer_Behavior.res.mjs +143 -0
  18. package/src/Customer/ReadModelStream/Customers.res +15 -0
  19. package/src/Customer/ReadModelStream/Customers.res.mjs +44 -0
  20. package/src/Customer/ReadModelStream/Customers_Projections.res +78 -0
  21. package/src/Customer/ReadModelStream/Customers_Projections.res.mjs +153 -0
  22. package/src/Extension/Products_Extension.res +23 -0
  23. package/src/Extension/Products_Extension.res.mjs +43 -0
  24. package/src/ExtensionPoint/Orders_ExtensionPointMapping.res +33 -0
  25. package/src/ExtensionPoint/Orders_ExtensionPointMapping.res.mjs +82 -0
  26. package/src/Order/AutomationSlice/AutoShipOrder.res +19 -0
  27. package/src/Order/AutomationSlice/AutoShipOrder.res.mjs +42 -0
  28. package/src/Order/AutomationSlice/AutoShipOrder_Automation.res +37 -0
  29. package/src/Order/AutomationSlice/AutoShipOrder_Automation.res.mjs +101 -0
  30. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res +22 -0
  31. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation.res.mjs +45 -0
  32. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res +24 -0
  33. package/src/Order/OutboundTranslationSlice/SendOrderConfirmation_Translation.res.mjs +46 -0
  34. package/src/Order/StateChangeSlice/CancelOrder.res +29 -0
  35. package/src/Order/StateChangeSlice/CancelOrder.res.mjs +75 -0
  36. package/src/Order/StateChangeSlice/CancelOrder_Behavior.res +35 -0
  37. package/src/Order/StateChangeSlice/CancelOrder_Behavior.res.mjs +110 -0
  38. package/src/Order/StateChangeSlice/PlaceOrder.res +28 -0
  39. package/src/Order/StateChangeSlice/PlaceOrder.res.mjs +63 -0
  40. package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res +39 -0
  41. package/src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs +70 -0
  42. package/src/Order/StateChangeSlice/RefundOrder.res +27 -0
  43. package/src/Order/StateChangeSlice/RefundOrder.res.mjs +59 -0
  44. package/src/Order/StateChangeSlice/RefundOrder_Behavior.res +26 -0
  45. package/src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs +77 -0
  46. package/src/Order/StateChangeSlice/ShipOrder.res +23 -0
  47. package/src/Order/StateChangeSlice/ShipOrder.res.mjs +56 -0
  48. package/src/Order/StateChangeSlice/ShipOrder_Behavior.res +26 -0
  49. package/src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs +73 -0
  50. package/src/Order/StateViewSliceStream/Orders.res +24 -0
  51. package/src/Order/StateViewSliceStream/Orders.res.mjs +80 -0
  52. package/src/Order/StateViewSliceStream/Orders_Projection.res +10 -0
  53. package/src/Order/StateViewSliceStream/Orders_Projection.res.mjs +52 -0
  54. package/src/Plugin.res +74 -0
  55. package/src/Plugin.res.mjs +304 -0
  56. package/src/Service/EmailService.res +3 -0
  57. package/src/Service/EmailService.res.mjs +11 -0
  58. package/src/plugin.json +1 -0
  59. package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res +27 -0
  60. package/tests/CatalogProduct/StateChangeSlice/SyncCatalogProduct_GWT.res.mjs +109 -0
  61. package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res +15 -0
  62. package/tests/CatalogProduct/StateViewSliceStream/AvailableProducts_GWT.res.mjs +93 -0
  63. package/tests/Customer/Aggregate/Customer_GWT.res +63 -0
  64. package/tests/Customer/Aggregate/Customer_GWT.res.mjs +153 -0
  65. package/tests/Customer/ReadModelStream/Customers_GWT.res +84 -0
  66. package/tests/Customer/ReadModelStream/Customers_GWT.res.mjs +90 -0
  67. package/tests/Extension/Products_Extension_GWT.res +23 -0
  68. package/tests/Extension/Products_Extension_GWT.res.mjs +108 -0
  69. package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res +23 -0
  70. package/tests/ExtensionPoint/Orders_ExtensionPointMapping_GWT.res.mjs +139 -0
  71. package/tests/Flow/OrderingFlow_GWT.res +69 -0
  72. package/tests/Flow/OrderingFlow_GWT.res.mjs +184 -0
  73. package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res +55 -0
  74. package/tests/Order/AutomationSlice/AutoShipOrder_GWT.res.mjs +133 -0
  75. package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res +30 -0
  76. package/tests/Order/OutboundTranslationSlice/SendOrderConfirmation_GWT.res.mjs +102 -0
  77. package/tests/Order/StateChangeSlice/CancelOrder_GWT.res +45 -0
  78. package/tests/Order/StateChangeSlice/CancelOrder_GWT.res.mjs +131 -0
  79. package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res +33 -0
  80. package/tests/Order/StateChangeSlice/PlaceOrder_GWT.res.mjs +139 -0
  81. package/tests/Order/StateChangeSlice/RefundOrder_GWT.res +31 -0
  82. package/tests/Order/StateChangeSlice/RefundOrder_GWT.res.mjs +100 -0
  83. package/tests/Order/StateChangeSlice/ShipOrder_GWT.res +27 -0
  84. package/tests/Order/StateChangeSlice/ShipOrder_GWT.res.mjs +91 -0
  85. package/tests/Order/StateViewSliceStream/Orders_GWT.res +36 -0
  86. package/tests/Order/StateViewSliceStream/Orders_GWT.res.mjs +114 -0
@@ -0,0 +1,28 @@
1
+ // PlaceOrder StateChangeSlice.
2
+ // Rejects duplicate placement and validates that all referenced products are
3
+ // available (synced to the ordering event log).
4
+
5
+ @@reventless.spec
6
+
7
+ @schema
8
+ type consumedEvent =
9
+ | OrderPlaced({orderId: string})
10
+ | CatalogProductSynced({productId: string})
11
+
12
+ @schema
13
+ type command =
14
+ PlaceOrder({
15
+ @partitionTag orderId: string,
16
+ // customerId is payload, not a query key — @noDcbTag stops it auto-tagging.
17
+ @noDcbTag customerId: string,
18
+ @ref("AvailableProducts") productIds: array<string>,
19
+ })
20
+
21
+ @schema
22
+ type error =
23
+ | OrderAlreadyPlaced
24
+ | ProductsNotAvailable({missing: array<string>})
25
+
26
+ @schema
27
+ type event =
28
+ OrderPlaced({@partitionTag orderId: string, customerId: string, productIds: array<string>})
@@ -0,0 +1,63 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
6
+
7
+ let consumedEventSchema = S.union([
8
+ S.schema(s => ({
9
+ TAG: "OrderPlaced",
10
+ orderId: s.m(DcbTag$Reventless.string)
11
+ })),
12
+ S.schema(s => ({
13
+ TAG: "CatalogProductSynced",
14
+ productId: s.m(DcbTag$Reventless.string)
15
+ }))
16
+ ]);
17
+
18
+ let commandSchema = S.schema(s => ({
19
+ TAG: "PlaceOrder",
20
+ orderId: s.m(DcbTag$Reventless.partition),
21
+ customerId: s.m(S.string),
22
+ productIds: s.m(S.array(Reference$Reventless.to_(undefined, "productId", "AvailableProducts")))
23
+ }));
24
+
25
+ let errorSchema = S.union([
26
+ S.literal("OrderAlreadyPlaced"),
27
+ S.schema(s => ({
28
+ TAG: "ProductsNotAvailable",
29
+ missing: s.m(S.array(S.string))
30
+ }))
31
+ ]);
32
+
33
+ let eventSchema = S.schema(s => ({
34
+ TAG: "OrderPlaced",
35
+ orderId: s.m(DcbTag$Reventless.partition),
36
+ customerId: s.m(DcbTag$Reventless.string),
37
+ productIds: s.m(S.array(DcbTag$Reventless.stringForKey("productId")))
38
+ }));
39
+
40
+ function commandAuthorization(param) {
41
+ return "AllowAuthenticated";
42
+ }
43
+
44
+ let name = "PlaceOrder";
45
+
46
+ let Id;
47
+
48
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/PlaceOrder.res.mjs";
49
+
50
+ let readConsistency = "EscalateOnRetry";
51
+
52
+ export {
53
+ name,
54
+ Id,
55
+ consumedEventSchema,
56
+ commandSchema,
57
+ errorSchema,
58
+ eventSchema,
59
+ moduleUrl,
60
+ commandAuthorization,
61
+ readConsistency,
62
+ }
63
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,39 @@
1
+ @@reventless.behavior
2
+
3
+ // Stored as immutable arrays (rather than `Set.t`) so each fold yields a fresh
4
+ // state — `evolve` returns a new record instead of mutating a shared one. This
5
+ // keeps successive `decide` calls (and unit tests) hermetic.
6
+ type state = {placedOrderIds: array<string>, availableProductIds: array<string>}
7
+
8
+ let initialState = {placedOrderIds: [], availableProductIds: []}
9
+
10
+ let evolve = (state, event: consumedEvent) =>
11
+ switch event {
12
+ | OrderPlaced({orderId}) => {
13
+ ...state,
14
+ placedOrderIds: state.placedOrderIds->Array.includes(orderId)
15
+ ? state.placedOrderIds
16
+ : Array.concat(state.placedOrderIds, [orderId]),
17
+ }
18
+ | CatalogProductSynced({productId}) => {
19
+ ...state,
20
+ availableProductIds: state.availableProductIds->Array.includes(productId)
21
+ ? state.availableProductIds
22
+ : Array.concat(state.availableProductIds, [productId]),
23
+ }
24
+ }
25
+
26
+ let decide = (state, command) =>
27
+ switch command {
28
+ | PlaceOrder({orderId, customerId, productIds}) =>
29
+ if state.placedOrderIds->Array.includes(orderId) {
30
+ Error(OrderAlreadyPlaced)
31
+ } else {
32
+ let missing = productIds->Array.filter(pid => !(state.availableProductIds->Array.includes(pid)))
33
+ if missing->Array.length > 0 {
34
+ Error(ProductsNotAvailable({missing: missing}))
35
+ } else {
36
+ Ok([OrderPlaced({orderId, customerId, productIds})])
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,70 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ let initialState_placedOrderIds = [];
5
+
6
+ let initialState_availableProductIds = [];
7
+
8
+ let initialState = {
9
+ placedOrderIds: initialState_placedOrderIds,
10
+ availableProductIds: initialState_availableProductIds
11
+ };
12
+
13
+ function evolve(state, event) {
14
+ if (event.TAG === "OrderPlaced") {
15
+ let orderId = event.orderId;
16
+ return {
17
+ placedOrderIds: state.placedOrderIds.includes(orderId) ? state.placedOrderIds : state.placedOrderIds.concat([orderId]),
18
+ availableProductIds: state.availableProductIds
19
+ };
20
+ }
21
+ let productId = event.productId;
22
+ return {
23
+ placedOrderIds: state.placedOrderIds,
24
+ availableProductIds: state.availableProductIds.includes(productId) ? state.availableProductIds : state.availableProductIds.concat([productId])
25
+ };
26
+ }
27
+
28
+ function decide(state, command) {
29
+ let orderId = command.orderId;
30
+ if (state.placedOrderIds.includes(orderId)) {
31
+ return {
32
+ TAG: "Error",
33
+ _0: "OrderAlreadyPlaced"
34
+ };
35
+ }
36
+ let productIds = command.productIds;
37
+ let missing = productIds.filter(pid => !state.availableProductIds.includes(pid));
38
+ if (missing.length !== 0) {
39
+ return {
40
+ TAG: "Error",
41
+ _0: {
42
+ TAG: "ProductsNotAvailable",
43
+ missing: missing
44
+ }
45
+ };
46
+ } else {
47
+ return {
48
+ TAG: "Ok",
49
+ _0: [{
50
+ TAG: "OrderPlaced",
51
+ orderId: orderId,
52
+ customerId: command.customerId,
53
+ productIds: productIds
54
+ }]
55
+ };
56
+ }
57
+ }
58
+
59
+ let Spec;
60
+
61
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/PlaceOrder_Behavior.res.mjs";
62
+
63
+ export {
64
+ Spec,
65
+ initialState,
66
+ evolve,
67
+ decide,
68
+ moduleUrl,
69
+ }
70
+ /* No side effect */
@@ -0,0 +1,27 @@
1
+ // RefundOrder StateChangeSlice.
2
+ // Handles internal refund processing — entirely hidden from public API.
3
+ // This is an automation/admin-only workflow triggered after cancellation.
4
+
5
+ @@reventless.spec
6
+
7
+ @schema
8
+ type consumedEvent =
9
+ | OrderPlaced
10
+ | OrderCancelled
11
+ | RefundIssued({reason: string})
12
+
13
+ @schema @noApi
14
+ type command = IssueRefund({orderId: string, reason: string})
15
+
16
+ @schema
17
+ type error =
18
+ | OrderNotFound
19
+ | OrderNotCancelled
20
+ | RefundAlreadyIssued
21
+
22
+ @schema
23
+ type event =
24
+ | RefundIssued({
25
+ orderId: string,
26
+ reason: string,
27
+ })
@@ -0,0 +1,59 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
6
+
7
+ let consumedEventSchema = S.union([
8
+ S.literal("OrderPlaced"),
9
+ S.literal("OrderCancelled"),
10
+ S.schema(s => ({
11
+ TAG: "RefundIssued",
12
+ reason: s.m(S.string)
13
+ }))
14
+ ]);
15
+
16
+ let commandSchema = S.schema(s => ({
17
+ TAG: "IssueRefund",
18
+ orderId: s.m(DcbTag$Reventless.string),
19
+ reason: s.m(S.string)
20
+ }));
21
+
22
+ let errorSchema = S.union([
23
+ S.literal("OrderNotFound"),
24
+ S.literal("OrderNotCancelled"),
25
+ S.literal("RefundAlreadyIssued")
26
+ ]);
27
+
28
+ let eventSchema = S.schema(s => ({
29
+ TAG: "RefundIssued",
30
+ orderId: s.m(DcbTag$Reventless.string),
31
+ reason: s.m(S.string)
32
+ }));
33
+
34
+ let commandSchema$1 = Api$ReventlessInfra.markNoApi(commandSchema);
35
+
36
+ function commandAuthorization(param) {
37
+ return "AllowAuthenticated";
38
+ }
39
+
40
+ let name = "RefundOrder";
41
+
42
+ let Id;
43
+
44
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/RefundOrder.res.mjs";
45
+
46
+ let readConsistency = "EscalateOnRetry";
47
+
48
+ export {
49
+ name,
50
+ Id,
51
+ consumedEventSchema,
52
+ errorSchema,
53
+ eventSchema,
54
+ commandSchema$1 as commandSchema,
55
+ moduleUrl,
56
+ commandAuthorization,
57
+ readConsistency,
58
+ }
59
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,26 @@
1
+ @@reventless.behavior
2
+
3
+ type state = {exists: bool, cancelled: bool, refunded: bool}
4
+
5
+ let initialState = {exists: false, cancelled: false, refunded: false}
6
+
7
+ let evolve = (state, event) =>
8
+ switch event {
9
+ | OrderPlaced => {exists: true, cancelled: false, refunded: false}
10
+ | OrderCancelled => {...state, cancelled: true}
11
+ | RefundIssued(_) => {...state, refunded: true}
12
+ }
13
+
14
+ let decide = (state, command) =>
15
+ switch command {
16
+ | IssueRefund({orderId, reason}) =>
17
+ if !state.exists {
18
+ Error(OrderNotFound)
19
+ } else if !state.cancelled {
20
+ Error(OrderNotCancelled)
21
+ } else if state.refunded {
22
+ Error(RefundAlreadyIssued)
23
+ } else {
24
+ Ok([RefundIssued({orderId, reason})])
25
+ }
26
+ }
@@ -0,0 +1,77 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function evolve(state, event) {
5
+ if (typeof event !== "object") {
6
+ if (event === "OrderPlaced") {
7
+ return {
8
+ exists: true,
9
+ cancelled: false,
10
+ refunded: false
11
+ };
12
+ } else {
13
+ return {
14
+ exists: state.exists,
15
+ cancelled: true,
16
+ refunded: state.refunded
17
+ };
18
+ }
19
+ } else {
20
+ return {
21
+ exists: state.exists,
22
+ cancelled: state.cancelled,
23
+ refunded: true
24
+ };
25
+ }
26
+ }
27
+
28
+ function decide(state, command) {
29
+ if (state.exists) {
30
+ if (state.cancelled) {
31
+ if (state.refunded) {
32
+ return {
33
+ TAG: "Error",
34
+ _0: "RefundAlreadyIssued"
35
+ };
36
+ } else {
37
+ return {
38
+ TAG: "Ok",
39
+ _0: [{
40
+ TAG: "RefundIssued",
41
+ orderId: command.orderId,
42
+ reason: command.reason
43
+ }]
44
+ };
45
+ }
46
+ } else {
47
+ return {
48
+ TAG: "Error",
49
+ _0: "OrderNotCancelled"
50
+ };
51
+ }
52
+ } else {
53
+ return {
54
+ TAG: "Error",
55
+ _0: "OrderNotFound"
56
+ };
57
+ }
58
+ }
59
+
60
+ let Spec;
61
+
62
+ let initialState = {
63
+ exists: false,
64
+ cancelled: false,
65
+ refunded: false
66
+ };
67
+
68
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/RefundOrder_Behavior.res.mjs";
69
+
70
+ export {
71
+ Spec,
72
+ initialState,
73
+ evolve,
74
+ decide,
75
+ moduleUrl,
76
+ }
77
+ /* No side effect */
@@ -0,0 +1,23 @@
1
+ // ShipOrder StateChangeSlice.
2
+ // Requires order to exist and not be cancelled; idempotent if already shipped.
3
+
4
+ @@reventless.spec
5
+
6
+ @schema
7
+ type consumedEvent =
8
+ | OrderPlaced
9
+ | OrderShipped
10
+ | OrderCancelled
11
+
12
+ @schema
13
+ type command =
14
+ | @allowedStates([Orders.Placed]) ShipOrder({orderId: string})
15
+
16
+ @schema
17
+ type error =
18
+ | OrderNotFound
19
+ | OrderAlreadyCancelled
20
+
21
+ @schema
22
+ type event =
23
+ | OrderShipped({orderId: string})
@@ -0,0 +1,56 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
6
+
7
+ let consumedEventSchema = S.union([
8
+ S.literal("OrderPlaced"),
9
+ S.literal("OrderShipped"),
10
+ S.literal("OrderCancelled")
11
+ ]);
12
+
13
+ let commandSchema = S.schema(s => ({
14
+ TAG: "ShipOrder",
15
+ orderId: s.m(DcbTag$Reventless.string)
16
+ }));
17
+
18
+ let errorSchema = S.union([
19
+ S.literal("OrderNotFound"),
20
+ S.literal("OrderAlreadyCancelled")
21
+ ]);
22
+
23
+ let eventSchema = S.schema(s => ({
24
+ TAG: "OrderShipped",
25
+ orderId: s.m(DcbTag$Reventless.string)
26
+ }));
27
+
28
+ let commandSchema$1 = Api$ReventlessInfra.markAllowedStates(commandSchema, [[
29
+ "ShipOrder",
30
+ ["Placed"]
31
+ ]]);
32
+
33
+ function commandAuthorization(param) {
34
+ return "AllowAuthenticated";
35
+ }
36
+
37
+ let name = "ShipOrder";
38
+
39
+ let Id;
40
+
41
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/ShipOrder.res.mjs";
42
+
43
+ let readConsistency = "EscalateOnRetry";
44
+
45
+ export {
46
+ name,
47
+ Id,
48
+ consumedEventSchema,
49
+ errorSchema,
50
+ eventSchema,
51
+ commandSchema$1 as commandSchema,
52
+ moduleUrl,
53
+ commandAuthorization,
54
+ readConsistency,
55
+ }
56
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,26 @@
1
+ @@reventless.behavior
2
+
3
+ type state = {exists: bool, shipped: bool, cancelled: bool}
4
+
5
+ let initialState = {exists: false, shipped: false, cancelled: false}
6
+
7
+ let evolve = (state, event) =>
8
+ switch event {
9
+ | OrderPlaced => {exists: true, shipped: false, cancelled: false}
10
+ | OrderShipped => {...state, shipped: true}
11
+ | OrderCancelled => {...state, cancelled: true}
12
+ }
13
+
14
+ let decide = (state, command) =>
15
+ switch command {
16
+ | ShipOrder({orderId: theId}) =>
17
+ if !state.exists {
18
+ Error(OrderNotFound)
19
+ } else if state.cancelled {
20
+ Error(OrderAlreadyCancelled)
21
+ } else if state.shipped {
22
+ Ok([]) // idempotent — already shipped
23
+ } else {
24
+ Ok([OrderShipped({orderId: theId})])
25
+ }
26
+ }
@@ -0,0 +1,73 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function evolve(state, event) {
5
+ switch (event) {
6
+ case "OrderPlaced" :
7
+ return {
8
+ exists: true,
9
+ shipped: false,
10
+ cancelled: false
11
+ };
12
+ case "OrderShipped" :
13
+ return {
14
+ exists: state.exists,
15
+ shipped: true,
16
+ cancelled: state.cancelled
17
+ };
18
+ case "OrderCancelled" :
19
+ return {
20
+ exists: state.exists,
21
+ shipped: state.shipped,
22
+ cancelled: true
23
+ };
24
+ }
25
+ }
26
+
27
+ function decide(state, command) {
28
+ if (state.exists) {
29
+ if (state.cancelled) {
30
+ return {
31
+ TAG: "Error",
32
+ _0: "OrderAlreadyCancelled"
33
+ };
34
+ } else if (state.shipped) {
35
+ return {
36
+ TAG: "Ok",
37
+ _0: []
38
+ };
39
+ } else {
40
+ return {
41
+ TAG: "Ok",
42
+ _0: [{
43
+ TAG: "OrderShipped",
44
+ orderId: command.orderId
45
+ }]
46
+ };
47
+ }
48
+ } else {
49
+ return {
50
+ TAG: "Error",
51
+ _0: "OrderNotFound"
52
+ };
53
+ }
54
+ }
55
+
56
+ let Spec;
57
+
58
+ let initialState = {
59
+ exists: false,
60
+ shipped: false,
61
+ cancelled: false
62
+ };
63
+
64
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateChangeSlice/ShipOrder_Behavior.res.mjs";
65
+
66
+ export {
67
+ Spec,
68
+ initialState,
69
+ evolve,
70
+ decide,
71
+ moduleUrl,
72
+ }
73
+ /* No side effect */
@@ -0,0 +1,24 @@
1
+ // Orders StateViewSliceStream.
2
+ // Projects order events from the shared ordering event log into an Orders read model.
3
+
4
+ @@reventless.spec
5
+
6
+ @schema
7
+ type consumedEvent =
8
+ | OrderPlaced({orderId: string, customerId: string, productIds: array<string>})
9
+ | OrderShipped({orderId: string})
10
+ | OrderCancelled({orderId: string})
11
+
12
+ @schema
13
+ type status =
14
+ | Placed
15
+ | Shipped
16
+ | Cancelled
17
+
18
+ @schema
19
+ type state = {
20
+ orderId: string,
21
+ customerId: string,
22
+ productIds: array<string>,
23
+ @status status: status,
24
+ }
@@ -0,0 +1,80 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
5
+ import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
6
+
7
+ let consumedEventSchema = S.union([
8
+ S.schema(s => ({
9
+ TAG: "OrderPlaced",
10
+ orderId: s.m(S.string),
11
+ customerId: s.m(S.string),
12
+ productIds: s.m(S.array(S.string))
13
+ })),
14
+ S.schema(s => ({
15
+ TAG: "OrderShipped",
16
+ orderId: s.m(S.string)
17
+ })),
18
+ S.schema(s => ({
19
+ TAG: "OrderCancelled",
20
+ orderId: s.m(S.string)
21
+ }))
22
+ ]);
23
+
24
+ let statusSchema = S.union([
25
+ S.literal("Placed"),
26
+ S.literal("Shipped"),
27
+ S.literal("Cancelled")
28
+ ]);
29
+
30
+ let stateSchema = S.schema(s => ({
31
+ orderId: s.m(S.string),
32
+ customerId: s.m(S.string),
33
+ productIds: s.m(S.array(S.string)),
34
+ status: s.m(statusSchema)
35
+ }));
36
+
37
+ let config = ReadModel$Reventless.config(undefined, undefined, undefined);
38
+
39
+ let stateSchema$1 = S.Metadata.set(stateSchema, StateAnnotations$Reventless.stateAnnotationsId, {
40
+ ids: [],
41
+ compositeIds: [],
42
+ subIds: [],
43
+ compositeSubIds: [],
44
+ indexes: [],
45
+ hidden: [],
46
+ summary: [],
47
+ drillTargets: [],
48
+ drillTargetKeys: [],
49
+ collapsed: [],
50
+ scan: [],
51
+ scanSort: [],
52
+ status: "status",
53
+ visibility: undefined
54
+ });
55
+
56
+ let name = "Orders";
57
+
58
+ let Id;
59
+
60
+ let subIdConfig;
61
+
62
+ let moduleUrl = "@reventlessdev/online-shop-hybrid-ordering/src/Order/StateViewSliceStream/Orders.res.mjs";
63
+
64
+ let authorization = "AllowAuthenticated";
65
+
66
+ let visibility = "Public";
67
+
68
+ export {
69
+ name,
70
+ Id,
71
+ consumedEventSchema,
72
+ statusSchema,
73
+ config,
74
+ subIdConfig,
75
+ stateSchema$1 as stateSchema,
76
+ moduleUrl,
77
+ authorization,
78
+ visibility,
79
+ }
80
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,10 @@
1
+ @@reventless.projection
2
+
3
+ let project = event =>
4
+ switch event {
5
+ | OrderPlaced({orderId, customerId, productIds}) => [
6
+ Set(orderId, {orderId, customerId, productIds, status: Placed}),
7
+ ]
8
+ | OrderShipped({orderId}) => [Update(orderId, state => {...state, status: Shipped})]
9
+ | OrderCancelled({orderId}) => [Update(orderId, state => {...state, status: Cancelled})]
10
+ }