Skip to content

Commit 61cffda

Browse files
authored
Merge pull request #17 from XerProjects/release-5.0.0
Release 5.0.0
2 parents 8b81644 + e858733 commit 61cffda

File tree

11 files changed

+358
-183
lines changed

11 files changed

+358
-183
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Xer.DomainDriven.Exceptions;
4+
5+
namespace Xer.DomainDriven
6+
{
7+
public abstract partial class AggregateRoot
8+
{
9+
protected interface IApplyActionConfiguration
10+
{
11+
/// <summary>
12+
/// Register an apply action to execute whenever a <typeparamref name="TDomainEvent"/> domain event is applied to the aggregate root.
13+
/// </summary>
14+
/// <typeparam name="TDomainEvent">Type of domain event to apply.</typeparam>
15+
IApplyActionSelector<TDomainEvent> Apply<TDomainEvent>() where TDomainEvent : class, IDomainEvent;
16+
17+
/// <summary>
18+
/// Require that an applier is registered for any domain events.
19+
/// </summary>
20+
void RequireApplyActions();
21+
22+
/// <summary>
23+
/// Set the action that executes whenever a domain event is successfully applied.
24+
/// </summary>
25+
/// <param name="onApplySuccess">Action to executes whenever a domain event is successfully applied.</param>
26+
void OnApplySuccess(Action<IDomainEvent> onApplySuccess);
27+
}
28+
29+
protected interface IApplyActionSelector<TDomainEvent> where TDomainEvent : class, IDomainEvent
30+
{
31+
/// <summary>
32+
/// Set apply action to apply the domain event.
33+
/// </summary>
34+
/// <param name="applyAction">Action to apply the domain event.</param>
35+
void With(Action<TDomainEvent> applyAction);
36+
}
37+
38+
protected interface IApplyActionResolver
39+
{
40+
/// <summary>
41+
/// Resolve apply action to execute for the domain event.
42+
/// </summary>
43+
/// <param name="domainEvent">Domain event to apply.</param>
44+
/// <exception cref="Xer.DomainDriven.Exceptions.DomainEventNotAppliedException">
45+
/// This exception might be thrown if <see cref="Xer.DomainDriven.AggregateRoot.IApplyActionConfiguration.RequireApplyActions"/>
46+
/// method was invoked and no registered domain event applier was found.
47+
/// </exception>
48+
/// <returns>Action that applies the domain event to the aggregate. Otherwise, null.</returns>
49+
Action<IDomainEvent> ResolveApplyActionFor(IDomainEvent domainEvent);
50+
}
51+
52+
private class ApplyActionConfiguration : IApplyActionConfiguration, IApplyActionResolver
53+
{
54+
#region Declarations
55+
56+
private readonly Dictionary<Type, Action<IDomainEvent>> _applyActionByDomainEventType = new Dictionary<Type, Action<IDomainEvent>>();
57+
private bool _requireDomainEventAppliers = false;
58+
private Action<IDomainEvent> _onApplySuccess = (e) => { };
59+
60+
#endregion Declarations
61+
62+
#region IApplyActionConfiguration Implementation
63+
64+
/// <summary>
65+
/// Register an apply action to execute whenever a <typeparamref name="TDomainEvent"/> domain event is applied to the aggregate root.
66+
/// </summary>
67+
/// <typeparam name="TDomainEvent">Type of domain event to apply.</typeparam>
68+
public IApplyActionSelector<TDomainEvent> Apply<TDomainEvent>() where TDomainEvent : class, IDomainEvent
69+
{
70+
return new ApplyActionSelector<TDomainEvent>(this);
71+
}
72+
73+
/// <summary>
74+
/// Require that an applier is registered for all domain events.
75+
/// </summary>
76+
public void RequireApplyActions()
77+
{
78+
_requireDomainEventAppliers = true;
79+
}
80+
81+
/// <summary>
82+
/// Set the action that executes each time a domain event is successfully applied.
83+
/// This overrides the any previously set action delegate.
84+
/// </summary>
85+
/// <param name="onDomainEventApplied">Action that executes each time a domain event is successfully applied.</param>
86+
public void OnApplySuccess(Action<IDomainEvent> onDomainEventApplied)
87+
{
88+
_onApplySuccess = onDomainEventApplied ?? throw new ArgumentNullException(nameof(onDomainEventApplied));
89+
}
90+
91+
#endregion IApplyActionConfiguration Implementation
92+
93+
#region IApplyActionResolver Implementation
94+
95+
/// <summary>
96+
/// Resolve action to execute for the applied domain event.
97+
/// </summary>
98+
/// <param name="domainEvent">Domain event to apply.</param>
99+
/// <exception cref="Xer.DomainDriven.Exceptions.DomainEventNotAppliedException">
100+
/// This exception will be thrown if <see cref="Xer.DomainDriven.AggregateRoot.IApplyActionConfiguration.RequireApplyActions"/>
101+
/// was invoked and no domain event applier can be resolved for the given domain event.
102+
/// </exception>
103+
/// <returns>Action that applies the domain event to the aggregate. Otherwise, null.</returns>
104+
public Action<IDomainEvent> ResolveApplyActionFor(IDomainEvent domainEvent)
105+
{
106+
if (domainEvent == null)
107+
{
108+
throw new ArgumentNullException(nameof(domainEvent));
109+
}
110+
111+
Type domainEventType = domainEvent.GetType();
112+
113+
bool found = _applyActionByDomainEventType.TryGetValue(domainEventType, out Action<IDomainEvent> applyAction);
114+
if (!found && _requireDomainEventAppliers)
115+
{
116+
throw new DomainEventNotAppliedException(domainEvent,
117+
$@"{GetType().Name} has no registered apply action for domain event of type {domainEventType.Name}.
118+
Configure domain event apply actions by calling {nameof(Configure)} method in constructor.");
119+
}
120+
121+
return applyAction;
122+
}
123+
124+
#endregion IApplyActionResolver Implementation
125+
126+
#region Methods
127+
128+
/// <summary>
129+
/// Register action to be executed for the domain event.
130+
/// </summary>
131+
/// <typeparam name="TDomainEvent">Type of domain event to apply.</typeparam>
132+
/// <param name="applyAction">Action to apply the domain event to the aggregate.</param>
133+
public void RegisterApplyAction<TDomainEvent>(Action<TDomainEvent> applyAction) where TDomainEvent : class, IDomainEvent
134+
{
135+
if (applyAction == null)
136+
{
137+
throw new ArgumentNullException(nameof(applyAction));
138+
}
139+
140+
Type domainEventType = typeof(TDomainEvent);
141+
142+
if (_applyActionByDomainEventType.ContainsKey(domainEventType))
143+
{
144+
throw new InvalidOperationException($"A apply action for {domainEventType.Name} has already been registered.");
145+
}
146+
147+
Action<IDomainEvent> apply = (e) =>
148+
{
149+
TDomainEvent domainEvent = e as TDomainEvent;
150+
if (domainEvent == null)
151+
{
152+
throw new ArgumentException(
153+
$@"An invalid domain event was provided to the apply action.
154+
Expected domain event is of type {typeof(TDomainEvent).Name} but {e.GetType().Name} was provided.");
155+
}
156+
157+
applyAction.Invoke(domainEvent);
158+
159+
_onApplySuccess.Invoke(domainEvent);
160+
};
161+
162+
_applyActionByDomainEventType.Add(domainEventType, apply);
163+
}
164+
165+
#endregion Methods
166+
}
167+
168+
private class ApplyActionSelector<TDomainEvent> : IApplyActionSelector<TDomainEvent> where TDomainEvent : class, IDomainEvent
169+
{
170+
private readonly ApplyActionConfiguration _configuration;
171+
172+
/// <summary>
173+
/// Constructor.
174+
/// </summary>
175+
/// <param name="configuration">Apply action configuration.</param>
176+
public ApplyActionSelector(ApplyActionConfiguration configuration)
177+
{
178+
_configuration = configuration;
179+
}
180+
181+
/// <summary>
182+
/// Set apply action tp apply the domain event.
183+
/// </summary>
184+
/// <param name="applyAction">Action to apply the domain event.</param>
185+
public void With(Action<TDomainEvent> applyAction)
186+
{
187+
if (applyAction == null)
188+
{
189+
throw new ArgumentNullException(nameof(applyAction));
190+
}
191+
192+
_configuration.RegisterApplyAction<TDomainEvent>(applyAction);
193+
}
194+
}
195+
}
196+
}

0 commit comments

Comments
 (0)