From 90b23a112c1af9f72f32e8d63daa422482fe62c6 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Tue, 8 Jan 2019 12:35:45 +0000 Subject: [PATCH] Apply OneOf to Todo; tidy --- equinox-web-csharp/Domain/Aggregate.cs | 8 +-- equinox-web-csharp/Domain/Domain.csproj | 2 +- equinox-web-csharp/Domain/Todo.cs | 81 +++++++++---------------- 3 files changed, 34 insertions(+), 57 deletions(-) diff --git a/equinox-web-csharp/Domain/Aggregate.cs b/equinox-web-csharp/Domain/Aggregate.cs index 2456e0f19..7de22a924 100644 --- a/equinox-web-csharp/Domain/Aggregate.cs +++ b/equinox-web-csharp/Domain/Aggregate.cs @@ -2,11 +2,11 @@ using Equinox.Store; using Microsoft.FSharp.Core; using Newtonsoft.Json; +using OneOf; using Serilog; using System; using System.Collections.Generic; using System.Threading.Tasks; -using OneOf; namespace TodoBackendTemplate { @@ -46,8 +46,8 @@ public class State static void Evolve(State s, Event x) => x.Match( - (Happened _) => s.Happened = true, - (Compacted e) => s.Happened = e.Happened); + (Event.Happened _) => s.Happened = true, + (Event.Compacted e) => s.Happened = e.Happened); public static State Fold(State origin, IEnumerable xs) { @@ -71,7 +71,7 @@ public class MakeItSo : Command } public static Event[] Interpret(State s, Command x) => - x.Match(makeItSo => + x.Match((MakeItSo _) => s.Happened ? new Event[0] : new Event [] { new Event.Happened()}); } diff --git a/equinox-web-csharp/Domain/Domain.csproj b/equinox-web-csharp/Domain/Domain.csproj index 5e789d257..c12f3e5a5 100755 --- a/equinox-web-csharp/Domain/Domain.csproj +++ b/equinox-web-csharp/Domain/Domain.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 7 + false diff --git a/equinox-web-csharp/Domain/Todo.cs b/equinox-web-csharp/Domain/Todo.cs index 7e12ecf53..316d2bf11 100644 --- a/equinox-web-csharp/Domain/Todo.cs +++ b/equinox-web-csharp/Domain/Todo.cs @@ -2,6 +2,7 @@ using Equinox.Store; using Microsoft.FSharp.Core; using Newtonsoft.Json; +using OneOf; using Serilog; using System; using System.Collections.Generic; @@ -14,6 +15,7 @@ public static class Todo { /// NB - these types and names reflect the actual storage formats and hence need to be versioned with care public abstract class Event + : OneOfBase { /// Information we retain per Todo List entry public abstract class ItemData @@ -95,37 +97,23 @@ public static State Fold(State origin, IEnumerable xs) var nextId = origin.NextId; var items = origin.Items.ToList(); foreach (var x in xs) - switch (x) - { - case Event.Added e: - nextId++; - items.Insert(0, e.Data); - break; - case Event.Updated e: + x.Switch( + (Event.Added e) => { nextId++; items.Insert(0, e.Data); }, + (Event.Updated e) => + { var i = items.FindIndex(item => item.Id == e.Data.Id); if (i != -1) items[i] = e.Data; - break; - case Event.Deleted e: - items.RemoveAll(item => item.Id == e.Id); - break; - case Event.Cleared e: - nextId = e.NextId; - items.Clear(); - break; - case Event.Compacted e: - nextId = e.NextId; - items = e.Items.ToList(); - break; - default: - throw new ArgumentOutOfRangeException(nameof(x), x, "invalid"); - } + }, + (Event.Deleted e) => items.RemoveAll(item => item.Id == e.Id), + (Event.Cleared e) => { nextId = e.NextId; items.Clear(); }, + (Event.Compacted e) => { nextId = e.NextId; items = e.Items.ToList(); }); return new State(nextId, items.ToArray()); } - + /// Determines whether a given event represents a checkpoint that implies we don't need to see any preceding events public static bool IsOrigin(Event e) => e is Event.Cleared || e is Event.Compacted; - + /// Prepares an Event that encodes all relevant aspects of a State such that `evolve` can rehydrate a complete State from it public static Event Compact(State state) => new Event.Compacted { NextId = state.NextId, Items = state.Items }; } @@ -140,6 +128,7 @@ public class Props /// Defines the operations a caller can perform on a Todo List public abstract class Command + : OneOfBase { /// Create a single item public class Add : Command @@ -166,38 +155,26 @@ public class Clear : Command } /// Defines the decision process which maps from the intent of the `Command` to the `Event`s that represent that decision in the Stream - public static IEnumerable Interpret(State s, Command x) - { - switch (x) - { - case Add c: - yield return Make(s.NextId, c.Props); - break; - case Update c: - var proposed = new {c.Props.Order, c.Props.Title, c.Props.Completed}; + public static IEnumerable Interpret(State s, Command x) => + x.Match( + (Add c) => new Event[] { Make(s.NextId, c.Props) }, + (Update c) => + { + var proposed = new { c.Props.Order, c.Props.Title, c.Props.Completed }; bool IsEquivalent(Event.ItemData i) => i.Id == c.Id - && new {i.Order, i.Title, i.Completed} == proposed; - - if (!s.Items.Any(IsEquivalent)) - yield return Make(c.Id, c.Props); - break; - case Delete c: - if (s.Items.Any(i => i.Id == c.Id)) - yield return new Event.Deleted {Id = c.Id}; - break; - case Clear _: - if (s.Items.Any()) yield return new Event.Cleared {NextId = s.NextId}; - break; - - default: - throw new ArgumentOutOfRangeException(nameof(x), x, "invalid"); - } + && new { i.Order, i.Title, i.Completed } == proposed; - T Make(int id, Props value) where T : Event.ItemEvent, new() => - new T {Data = {Id = id, Order = value.Order, Title = value.Title, Completed = value.Completed}}; - } + if (s.Items.Any(IsEquivalent)) + return Enumerable.Empty(); + return new[] { Make(c.Id, c.Props) }; + }, + (Delete c) => s.Items.Any(i => i.Id == c.Id) ? new Event[] { new Event.Deleted { Id = c.Id } } : new Event[0], + (Clear _) => s.Items.Any() ? new Event[] { new Event.Cleared { NextId = s.NextId } } : new Event[0]); + + static T Make(int id, Props value) where T : Event.ItemEvent, new() => + new T { Data = { Id = id, Order = value.Order, Title = value.Title, Completed = value.Completed } }; } /// Defines low level stream operations relevant to the Todo Stream in terms of Command and Events