Skip to content

Commit 5e657c2

Browse files
committed
Apply OneOf to Todo; tidy
1 parent ebbf848 commit 5e657c2

File tree

2 files changed

+35
-58
lines changed

2 files changed

+35
-58
lines changed

equinox-web-csharp/Domain/Aggregate.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public class State
4646

4747
static void Evolve(State s, Event x) =>
4848
x.Match(
49-
(Happened _) => s.Happened = true,
50-
(Compacted e) => s.Happened = e.Happened);
49+
(Event.Happened _) => s.Happened = true,
50+
(Event.Compacted e) => s.Happened = e.Happened);
5151

5252
public static State Fold(State origin, IEnumerable<Event> xs)
5353
{
@@ -71,7 +71,7 @@ public class MakeItSo : Command
7171
}
7272

7373
public static Event[] Interpret(State s, Command x) =>
74-
x.Match(makeItSo =>
74+
x.Match((MakeItSo _) =>
7575
s.Happened ? new Event[0] : new Event [] { new Event.Happened()});
7676
}
7777

equinox-web-csharp/Domain/Todo.cs

Lines changed: 32 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Equinox.Store;
33
using Microsoft.FSharp.Core;
44
using Newtonsoft.Json;
5+
using OneOf;
56
using Serilog;
67
using System;
78
using System.Collections.Generic;
@@ -14,6 +15,7 @@ public static class Todo
1415
{
1516
/// NB - these types and names reflect the actual storage formats and hence need to be versioned with care
1617
public abstract class Event
18+
: OneOfBase<Event.Added, Event.Updated, Event.Deleted, Event.Cleared, Event.Compacted>
1719
{
1820
/// Information we retain per Todo List entry
1921
public abstract class ItemData
@@ -95,37 +97,23 @@ public static State Fold(State origin, IEnumerable<Event> xs)
9597
var nextId = origin.NextId;
9698
var items = origin.Items.ToList();
9799
foreach (var x in xs)
98-
switch (x)
99-
{
100-
case Event.Added e:
101-
nextId++;
102-
items.Insert(0, e.Data);
103-
break;
104-
case Event.Updated e:
100+
x.Switch(
101+
(Event.Added e) => { nextId++; items.Insert(0, e.Data); },
102+
(Event.Updated e) =>
103+
{
105104
var i = items.FindIndex(item => item.Id == e.Data.Id);
106105
if (i != -1)
107106
items[i] = e.Data;
108-
break;
109-
case Event.Deleted e:
110-
items.RemoveAll(item => item.Id == e.Id);
111-
break;
112-
case Event.Cleared e:
113-
nextId = e.NextId;
114-
items.Clear();
115-
break;
116-
case Event.Compacted e:
117-
nextId = e.NextId;
118-
items = e.Items.ToList();
119-
break;
120-
default:
121-
throw new ArgumentOutOfRangeException(nameof(x), x, "invalid");
122-
}
107+
},
108+
(Event.Deleted e) => items.RemoveAll(item => item.Id == e.Id),
109+
(Event.Cleared e) => { nextId = e.NextId; items.Clear(); },
110+
(Event.Compacted e) => { nextId = e.NextId; items = e.Items.ToList(); });
123111
return new State(nextId, items.ToArray());
124112
}
125-
113+
126114
/// Determines whether a given event represents a checkpoint that implies we don't need to see any preceding events
127115
public static bool IsOrigin(Event e) => e is Event.Cleared || e is Event.Compacted;
128-
116+
129117
/// Prepares an Event that encodes all relevant aspects of a State such that `evolve` can rehydrate a complete State from it
130118
public static Event Compact(State state) => new Event.Compacted { NextId = state.NextId, Items = state.Items };
131119
}
@@ -140,6 +128,7 @@ public class Props
140128

141129
/// Defines the operations a caller can perform on a Todo List
142130
public abstract class Command
131+
: OneOfBase<Command.Add, Command.Update, Command.Delete, Command.Clear>
143132
{
144133
/// Create a single item
145134
public class Add : Command
@@ -166,38 +155,26 @@ public class Clear : Command
166155
}
167156

168157
/// Defines the decision process which maps from the intent of the `Command` to the `Event`s that represent that decision in the Stream
169-
public static IEnumerable<Event> Interpret(State s, Command x)
170-
{
171-
switch (x)
172-
{
173-
case Add c:
174-
yield return Make<Event.Added>(s.NextId, c.Props);
175-
break;
176-
case Update c:
177-
var proposed = new {c.Props.Order, c.Props.Title, c.Props.Completed};
158+
public static IEnumerable<Event> Interpret(State s, Command x) =>
159+
x.Match(
160+
(Add c) => new Event[] { Make<Event.Added>(s.NextId, c.Props) },
161+
(Update c) =>
162+
{
163+
var proposed = new { c.Props.Order, c.Props.Title, c.Props.Completed };
178164

179165
bool IsEquivalent(Event.ItemData i) =>
180166
i.Id == c.Id
181-
&& new {i.Order, i.Title, i.Completed} == proposed;
182-
183-
if (!s.Items.Any(IsEquivalent))
184-
yield return Make<Event.Updated>(c.Id, c.Props);
185-
break;
186-
case Delete c:
187-
if (s.Items.Any(i => i.Id == c.Id))
188-
yield return new Event.Deleted {Id = c.Id};
189-
break;
190-
case Clear _:
191-
if (s.Items.Any()) yield return new Event.Cleared {NextId = s.NextId};
192-
break;
193-
194-
default:
195-
throw new ArgumentOutOfRangeException(nameof(x), x, "invalid");
196-
}
167+
&& new { i.Order, i.Title, i.Completed } == proposed;
197168

198-
T Make<T>(int id, Props value) where T : Event.ItemEvent, new() =>
199-
new T {Data = {Id = id, Order = value.Order, Title = value.Title, Completed = value.Completed}};
200-
}
169+
if (s.Items.Any(IsEquivalent))
170+
return Enumerable.Empty<Event>();
171+
return new[] { Make<Event.Updated>(c.Id, c.Props) };
172+
},
173+
(Delete c) => s.Items.Any(i => i.Id == c.Id) ? new Event[] { new Event.Deleted { Id = c.Id } } : new Event[0],
174+
(Clear _) => s.Items.Any() ? new Event[] { new Event.Cleared { NextId = s.NextId } } : new Event[0]);
175+
176+
static T Make<T>(int id, Props value) where T : Event.ItemEvent, new() =>
177+
new T { Data = { Id = id, Order = value.Order, Title = value.Title, Completed = value.Completed } };
201178
}
202179

203180
/// Defines low level stream operations relevant to the Todo Stream in terms of Command and Events
@@ -279,19 +256,19 @@ public Task<Unit> Execute(ClientId clientId, Command command) =>
279256
/// Create a new ToDo List item; response contains the generated `id`
280257
public async Task<View> Create(ClientId clientId, Props template)
281258
{
282-
var state = await _stream(clientId).Decide(new Command.Add {Props = template});
259+
var state = await _stream(clientId).Decide(new Command.Add { Props = template });
283260
return Render(state.First());
284261
}
285262

286263
/// Update the specified item as referenced by the `item.id`
287264
public async Task<View> Patch(ClientId clientId, int id, Props value)
288265
{
289-
var state = await _stream(clientId).Decide(new Command.Update {Id = id, Props = value});
266+
var state = await _stream(clientId).Decide(new Command.Update { Id = id, Props = value });
290267
return Render(state.Single(x => x.Id == id));
291268
}
292269

293270
static View Render(Event.ItemData i) =>
294-
new View {Id = i.Id, Order = i.Order, Title = i.Title, Completed = i.Completed};
271+
new View { Id = i.Id, Order = i.Order, Title = i.Title, Completed = i.Completed };
295272
}
296273
}
297274
}

0 commit comments

Comments
 (0)