Skip to content

Instantly share code, notes, and snippets.

View MarcoNicolodi's full-sized avatar

Marco Nicolodi MarcoNicolodi

View GitHub Profile
@MarcoNicolodi
MarcoNicolodi / ProposalService.cs
Created October 11, 2019 01:12
ProposalService now has domain logic leaking again
public class ProposalService
{
public void AcceptProposal(Guid proposalId)
{
var proposal = _repository.Find(proposalId);
//calling Accept method instead of changing its properties
proposal.Accept();
_repository.Save(proposal);
var proposalsToDeny = _repository.FindOpenForCandidate(proposal.CandidateId);
@MarcoNicolodi
MarcoNicolodi / Program.cs
Created October 9, 2019 11:23
Showing how to refer to other aggregates
//this would let the Proposal aggregate to operate on other aggregates
public class Proposal
{
public Candidate Candidate { get; }
public JobListing JobListing { get; }
}
//this is why we should refer to other aggregates by id
public class Proposal
{
@MarcoNicolodi
MarcoNicolodi / Program.cs
Last active November 29, 2019 16:08
Saving a non aggregate root may violate invariants
var inconsistentComment = new Comment(closedProposalId, anyCandidateIdIWant, content);
_commentsRepository.Save(comment);
@MarcoNicolodi
MarcoNicolodi / Proposal.cs
Last active November 29, 2019 17:54
Proposal aggregate maintains invariants about comments
public class Proposal
{
private List<Comments> _comments;
public IReadOnlyList<Comments> Comments => _comments.AsReadOnly();
private bool IsOpenForComments => Status == ProposalStatus.Pending;
public Guid CandidateId { get; }
public Guid JobListingId { get; }
public ProposaStatus Status { get; private set; }
public DateTime ClosedAt { get; private set; }
public class Comment
{
public Guid ProposalId { get; }
public Guid UserId { get; }
public string Content { get; private set; }
public Comment(Guid proposalId, Guid userId, string content)
{
if(String.IsNullOrWhiteSpace(content))
throw new DomainException("Empty comments not allowed");
@MarcoNicolodi
MarcoNicolodi / ProposalService.cs
Last active October 4, 2019 00:50
ProposalService orchestrating a rich domain model
public class ProposalService
{
public void AcceptProposal(Guid proposalId)
{
var proposal = _repository.Find(proposalId);
//calling Accept method instead of changing its properties
proposal.Accept();
_repository.Save(proposal);
_logger.Info($"Proposal {proposal.id} was accepted");
}
@MarcoNicolodi
MarcoNicolodi / ProposalService.cs
Last active October 5, 2019 18:41
ProposalServive with domain knowledge leak
public class ProposalService
{
public void AcceptProposal(Guid proposalId)
{
//retrieve proposal aggregate from repository
var proposal = _repository.Find(proposalId);
//if proposal is expired, fail fast
if(DateTime.Now.Substract(proprosal.SentAt).TotalDays > 15)
throw new DomainException("This proprosal has expired");
@MarcoNicolodi
MarcoNicolodi / Program.cs
Created September 28, 2019 18:22
Console application using AccountService
class Program
{
static void Main(string[] args)
{
var command = _mapper.ToWithrawCommand(args);
_accountService.WithdrawAmount(command);
}
}
@MarcoNicolodi
MarcoNicolodi / AccountController.cs
Created September 28, 2019 17:56
AccountController calling AccountService
[Route("api/[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
[HttpPost("{id}/withdraw")]
public ActionResult WithdrawAmount(WithdrawAmountCommand command)
{
_accountService.WithdrawAmount(command);
return Ok()
}
@MarcoNicolodi
MarcoNicolodi / AccountService.cs
Last active September 28, 2019 17:54
Controller code extracted to application service
public class AccountService
{
public void WithdrawAmount(WithdrawAmountCommand command)
{
var account = _repository.Find(command.AccountId);
account.Withdraw(command.Amount);
_logger.Info($"Client {account.OwnerId} withdrew {command.Amount}");
_broker.Send(new AmountWithdrewEvent(account.Id, command.Amount));
}
}