15. UI (HTML + Angular)
WebAPI
RavenDB
Presentation
Business
Persistence
16. Esse modelo não está anêmico?
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public Address HomeAddress { get; set; }
public decimal Salary { get; set; }
}
17. Cadê a linguagem ubíqua?
[Route("api/[controller]")]
public class EmployeesController : Controller
{
[HttpGet]
public IEnumerable<Employee> Get()
{ /* .. */ }
[HttpGet("{id}")]
public Employee Get(string id)
{ /* .. */ }
[HttpPost]
public void Post([FromBody]Employee value)
{ /* .. */ }
[HttpPut("{id}")]
public void Put(string id, [FromBody]Employee value)
{ /* .. */ }
[HttpDelete("{id}")]
public void Delete(string id)
{ /* .. */}
}
51. Anatomia de um Evento
public class EmployeeHomeAddressChangedEvent
: VersionedEvent<Guid>
{
public Address NewAddress { get; }
public EmployeeHomeAddressChangedEvent(Address newAddress)
{
NewAddress = newAddress;
}
}
public class VersionedEvent<TSourceId> : IVersionedEvent<TSourceId>
{
public TSourceId SourceId { get; internal set; }
public DateTime When { get; private set; }
public int Version { get; internal set; }
public VersionedEvent()
{
When = DateTime.Now;
}
}
52. Anatomia de um Evento
public class EmployeeHomeAddressChangedEvent
: VersionedEvent<Guid>
{
public Address NewAddress { get; }
public EmployeeHomeAddressChangedEvent(Address newAddress)
{
NewAddress = newAddress;
}
}
public class VersionedEvent<TSourceId> : IVersionedEvent<TSourceId>
{
public TSourceId SourceId { get; internal set; }
public DateTime When { get; private set; }
public int Version { get; internal set; }
public VersionedEvent()
{
When = DateTime.Now;
}
}
53. Anatomia de um Evento
public class EmployeeHomeAddressChangedEvent
: VersionedEvent<Guid>
{
public Address NewAddress { get; }
public EmployeeHomeAddressChangedEvent(Address newAddress)
{
NewAddress = newAddress;
}
}
public class VersionedEvent<TSourceId> : IVersionedEvent<TSourceId>
{
public TSourceId SourceId { get; internal set; }
public DateTime When { get; private set; }
public int Version { get; internal set; }
public VersionedEvent()
{
When = DateTime.Now;
}
}
60. Entidade/Agregado gera Eventos
public void RaiseSalary(decimal amount)
{
Throw.IfArgumentIsNegative(amount, nameof(amount));
Update(new EmployeeSalaryRaisedEvent(amount));
}
61. Entidade/Agregado gera Eventos
public void ChangeHomeAddress(Address address)
{
Throw.IfArgumentIsNull(address, nameof(address));
Update(new EmployeeHomeAddressChangedEvent(address));
}
62. Atualiza estado a partir dos
Eventos
protected void Update(VersionedEvent<TId> e)
{
e.SourceId = Id;
e.Version = Version + 1;
_handlers[e.GetType()].Invoke(e);
Version = e.Version;
_pendingEvents.Add(e);
}
63. Atualiza estado a partir dos
Eventos
protected void Update(VersionedEvent<TId> e)
{
e.SourceId = Id;
e.Version = Version + 1;
_handlers[e.GetType()].Invoke(e);
Version = e.Version;
_pendingEvents.Add(e);
}
private Employee(Guid id) : base(id)
{
Handles<EmployeeRegisteredEvent>(OnEmployeeRegistered);
Handles<EmployeeSalaryRaisedEvent>(OnEmployeeSalaryRaised);
Handles<EmployeeHomeAddressChangedEvent>(OnEmployeeHomeAddressChanged);
}
64. Atualiza estado a partir dos
Eventos
protected void Update(VersionedEvent<TId> e)
{
e.SourceId = Id;
e.Version = Version + 1;
_handlers[e.GetType()].Invoke(e);
Version = e.Version;
_pendingEvents.Add(e);
}
private void OnEmployeeRegistered(EmployeeRegisteredEvent @event)
{
Name = @event.Name;
Salary = @event.InitialSalary;
}
private void OnEmployeeSalaryRaised(EmployeeSalaryRaisedEvent @event)
{
Salary += @event.Amount;
}
65. Atualiza estado a partir dos
Eventos
protected void Update(VersionedEvent<TId> e)
{
e.SourceId = Id;
e.Version = Version + 1;
_handlers[e.GetType()].Invoke(e);
Version = e.Version;
_pendingEvents.Add(e);
}
70. Salvar um documento com
eventospublic void Save(Employee employee)
{
var head = GetHead(employee.Id);
var storedVersion = GetStoredVersionOf(head);
if (storedVersion != (employee.Version - employee.PendingEvents.Count()))
throw new InvalidOperationException("Invalid object state.");
if (head == null)
{
SaveNewEmployee(employee);
}
else
{
SaveEmployeeEvents(employee);
}
foreach (var evt in employee.PendingEvents)
Bus.RaiseEvent(evt);
}
71. Salvar um documento com
eventos
public JsonDocumentMetadata GetHead(Guid id)
{
string localId = $"employees/{id}";
return _store.DatabaseCommands.Head(localId);
}
public int GetStoredVersionOf(JsonDocumentMetadata
head)
{
return head
?.Metadata[EmployeeEntityVersion]
.Value<int>() ?? 0;
}
72. Salvar um documento com
eventospublic void Save(Employee employee)
{
var head = GetHead(employee.Id);
var storedVersion = GetStoredVersionOf(head);
if (storedVersion != (employee.Version - employee.PendingEvents.Count()))
throw new InvalidOperationException("Invalid object state.");
if (head == null)
{
SaveNewEmployee(employee);
}
else
{
SaveEmployeeEvents(employee);
}
foreach (var evt in employee.PendingEvents)
Bus.RaiseEvent(evt);
}
73. Salvar um documento com
eventos
private void SaveNewEmployee(Employee employee)
{
using (var session = _store.OpenSession())
{
var document = new EmployeeEvents(
employee.Id,
employee.PendingEvents
);
session.Store(document);
session.Advanced.GetMetadataFor(document)
.Add(EmployeeEntityVersion, employee.Version);
session.SaveChanges();
}
}
74. private void SaveEmployeeEvents(
Employee employee )
{
var patches = new List<PatchRequest>();
foreach (var evt in employee.PendingEvents)
{
patches.Add(new PatchRequest
{
Type = PatchCommandType.Add,
Name = "Events",
Value = RavenJObject.FromObject(evt, _serializer)
});
}
var localId = $"employees/{employee.Id}";
var addEmployeeEvents = new PatchCommandData()
{
Key = localId,
Patches = patches.ToArray()
};
var updateMetadata = new ScriptedPatchCommandData()
{
Key = localId,
Patch = new ScriptedPatchRequest
{
Script = $"this['@metadata']['{EmployeeEntityVersion}'] = {employee.Version}; "
}
};
_store.DatabaseCommands.Batch(new ICommandData[]
{
addEmployeeEvents,
updateMetadata
});
}
75. private void SaveEmployeeEvents(
Employee employee )
{
var patches = new List<PatchRequest>();
foreach (var evt in employee.PendingEvents)
{
patches.Add(new PatchRequest
{
Type = PatchCommandType.Add,
Name = "Events",
Value = RavenJObject.FromObject(evt, _serializer)
});
}
var localId = $"employees/{employee.Id}";
var addEmployeeEvents = new PatchCommandData()
{
Key = localId,
Patches = patches.ToArray()
};
var updateMetadata = new ScriptedPatchCommandData()
{
Key = localId,
Patch = new ScriptedPatchRequest
{
Script = $"this['@metadata']['{EmployeeEntityVersion}'] = {employee.Version}; "
}
};
_store.DatabaseCommands.Batch(new ICommandData[]
{
addEmployeeEvents,
updateMetadata
});
}
76. private void SaveEmployeeEvents(
Employee employee )
{
var patches = new List<PatchRequest>();
foreach (var evt in employee.PendingEvents)
{
patches.Add(new PatchRequest
{
Type = PatchCommandType.Add,
Name = "Events",
Value = RavenJObject.FromObject(evt, _serializer)
});
}
var localId = $"employees/{employee.Id}";
var addEmployeeEvents = new PatchCommandData()
{
Key = localId,
Patches = patches.ToArray()
};
var updateMetadata = new ScriptedPatchCommandData()
{
Key = localId,
Patch = new ScriptedPatchRequest
{
Script = $"this['@metadata']['{EmployeeEntityVersion}'] = {employee.Version}; "
}
};
_store.DatabaseCommands.Batch(new ICommandData[]
{
addEmployeeEvents,
updateMetadata
});
}
77. Salvar um documento com
eventospublic void Save(Employee employee)
{
var head = GetHead(employee.Id);
var storedVersion = GetStoredVersionOf(head);
if (storedVersion != (employee.Version - employee.PendingEvents.Count()))
throw new InvalidOperationException("Invalid object state.");
if (head == null)
{
SaveNewEmployee(employee);
}
else
{
SaveEmployeeEvents(employee);
}
foreach (var evt in employee.PendingEvents)
Bus.RaiseEvent(evt);
}
78. Carregar um documento com
eventos
public Employee Load(Guid id)
{
EmployeeEvents data;
using (var session = _store.OpenSession())
{
data = session.Load<EmployeeEvents>(id);
}
return new Employee(id, data.Events);
}
public Employee(Guid id,
IEnumerable<IVersionedEvent<Guid>> history)
: this(id)
{
LoadFrom(history);
}
79. Carregar um documento com
eventos
protected void LoadFrom(IEnumerable<IVersionedEvent<TId>> pastEvents)
{
foreach (var e in pastEvents)
{
_handlers[e.GetType()].Invoke(e);
Version = e.Version;
}
}