Skip to content

Commit

Permalink
feat(groups): implemented group creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Verdejo committed Nov 16, 2023
1 parent 1140e93 commit 814cfc4
Show file tree
Hide file tree
Showing 36 changed files with 609 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Application/Budgets/Commands/CreateBudgetCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

namespace Application.Budgets;

public sealed record CreateBudgetCommand(Guid Id, Decimal MaximumAmount, Currency Currency) : ICommand;
public sealed record CreateBudgetCommand(Guid Id, Decimal MaximumAmount, Currency Currency, Guid Owner) : ICommand;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Domain.Budgets;
using Application.Shared.Command;
using Domain;

namespace Application.Budgets;

Expand All @@ -17,8 +18,9 @@ public async Task Handle(CreateBudgetCommand request, CancellationToken cancella
var id = BudgetId.Create(request.Id);
var amount = BudgetAmount.Create(request.MaximumAmount, request.Currency);
var period = BudgetPeriod.CreateMonthly();
var owner = UserId.Create(request.Owner);

var budget = Budget.Create(id, amount, period);
var budget = Budget.Create(id, amount, period, owner);

await _budgetRepository.Add(budget);
}
Expand Down
2 changes: 1 addition & 1 deletion Application/Expenses/Commands/CreateExpenseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

namespace Application.Expenses.Commands;

public sealed record CreateExpenseCommand(Guid Id, decimal Amount, Currency Currency, string Description) : ICommand;
public sealed record CreateExpenseCommand(Guid Id, decimal Amount, Currency Currency, string Description, Guid User) : ICommand;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Application.Shared.Command;
using Domain;
using Domain.Expenses;

namespace Application.Expenses.Commands;
Expand All @@ -17,10 +18,11 @@ public async Task Handle(CreateExpenseCommand request, CancellationToken cancell
var id = ExpenseId.Create(request.Id);
var amount = ExpenseAmount.Create(request.Amount, request.Currency);
var description = ExpenseDescription.Create(request.Description);
var user = UserId.Create(request.User);

var expense = Expense.Create(id: id, amount: amount, description: description);
var expense = Expense.Create(id: id, amount: amount, description: description, user: user);

await this._expenseRepository.Add(expense);
await _expenseRepository.Add(expense);
}
}

5 changes: 5 additions & 0 deletions Application/Groups/CreateGroupCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Application.Shared.Command;

namespace Application;

public sealed record CreateGroupCommand(Guid GroupId, string GroupName, Guid UserId) : ICommand;
26 changes: 26 additions & 0 deletions Application/Groups/Handlers/CreateGroupCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Application.Shared.Command;
using Domain;
using Domain.Groups;

namespace Application;

public class CreateGroupCommandHandler : ICommandHandler<CreateGroupCommand>
{
private readonly IGroupRepository _groupRepository;

public CreateGroupCommandHandler(IGroupRepository groupRepository)
{
_groupRepository = groupRepository;
}

public async Task Handle(CreateGroupCommand request, CancellationToken cancellationToken = default)
{
var name = GroupName.Create(request.GroupName);
var id = GroupId.Create(request.GroupId);
var admin = UserId.Create(request.UserId);

var group = Group.Create(id, name, admin);

await _groupRepository.Add(group);
}
}
11 changes: 7 additions & 4 deletions Domain/Budgets/Entities/Budget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public class Budget : AggregateRoot<Budget>
#region Properties
public BudgetId Id { get; private set; }

public UserId Owner { get; private set; }

public BudgetAmount MaximumAmount { get; private set; }

private decimal RemainingAmountAfter(Decimal amount) => MaximumAmount.Amount - Records.Sum(r => r.Amount) - amount;
Expand All @@ -27,18 +29,19 @@ private Budget()
{
}

protected Budget(BudgetId id, BudgetAmount maximumAmount, BudgetPeriod period)
protected Budget(BudgetId id, BudgetAmount maximumAmount, BudgetPeriod period, UserId owner)
{
Id = id;
MaximumAmount = maximumAmount;
Period = period;
Owner = owner;
}

public static Budget Create(BudgetId id, BudgetAmount maximumAmount, BudgetPeriod period)
public static Budget Create(BudgetId id, BudgetAmount maximumAmount, BudgetPeriod period, UserId owner)
{
var budget = new Budget(id, maximumAmount, period);
var budget = new Budget(id, maximumAmount, period, owner);

budget.RecordEvent(new BudgetCreatedEvent(id.Value));
budget.RecordEvent(new BudgetCreatedEvent(id.Value, owner.Value));

return budget;
}
Expand Down
2 changes: 1 addition & 1 deletion Domain/Budgets/Events/BudgetCreatedEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

namespace Domain.Budgets;

public sealed record BudgetCreatedEvent(Guid Id) : IDomainEvent;
public sealed record BudgetCreatedEvent(Guid Id, Guid OwnerId) : IDomainEvent;
11 changes: 7 additions & 4 deletions Domain/Expenses/Entities/Expense.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public class Expense : AggregateRoot<Expense>
{
public ExpenseId Id { get; private set; }

public UserId User { get; private set; }

public ExpenseAmount Amount { get; private set; }

public ExpenseDescription Description { get; private set; }
Expand All @@ -14,18 +16,19 @@ private Expense()
{
}

protected Expense(ExpenseId id, ExpenseAmount amount, ExpenseDescription description)
protected Expense(ExpenseId id, ExpenseAmount amount, ExpenseDescription description, UserId user)
{
Id = id;
Amount = amount;
Description = description;
User = user;
}

public static Expense Create(ExpenseId id, ExpenseAmount amount, ExpenseDescription description)
public static Expense Create(ExpenseId id, ExpenseAmount amount, ExpenseDescription description, UserId user)
{
var expense = new Expense(id: id, amount: amount, description: description);
var expense = new Expense(id: id, amount: amount, description: description, user: user);

expense.RecordEvent(new ExpenseCreatedEvent(id.Value, amount.Quantity, DateTime.Now));
expense.RecordEvent(new ExpenseCreatedEvent(id.Value, amount.Quantity, DateTime.Now, user.Value));

return expense;
}
Expand Down
13 changes: 7 additions & 6 deletions Domain/Expenses/Events/ExpenseCreatedEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@

namespace Domain.Expenses;

public sealed record ExpenseCreatedEvent : IDomainEvent
public sealed record ExpenseCreatedEvent : IDomainEvent
{
public Guid Id { get; set; }

public Decimal Amount { get; set; }

public DateTime CreatedAt { get; set; }


public Guid User { get; set; }

public ExpenseCreatedEvent()
{
}
public ExpenseCreatedEvent(Guid id, Decimal amount, DateTime createdAt)

public ExpenseCreatedEvent(Guid id, Decimal amount, DateTime createdAt, Guid user)
{
Id = id;
Amount = amount;
CreatedAt = createdAt;
}

User = user;
}
}
42 changes: 42 additions & 0 deletions Domain/Groups/Entities/Group.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Domain.Shared.Base;

namespace Domain.Groups;

public class Group : AggregateRoot<Group>
{
public GroupId Id { get; private set; }

public GroupName Name { get; private set; }

public UserId Admin { get; private set; }

public HashSet<UserId> Members { get; private set; } = new();

public List<GroupRecords> Records { get; private set; } = new();

private Group()
{
}

protected Group(GroupId id, GroupName name, UserId admin)
{
}

public static Group Create(GroupId id, GroupName name, UserId admin)
{
var group = new Group(id, name, admin);

group.RecordEvent(new GroupCreatedEvent(id.Value, name.Value, admin.Value));

return group;
}

public void Join(UserId user)
{
if (Members.Contains(user) is true)
throw new AlreadyMemberException(user, this);

Members.Add(user);
RecordEvent(new MemberJoinedEvent(user.Value, this.Id.Value));
}
}
10 changes: 10 additions & 0 deletions Domain/Groups/Errors/AlreadyMemberException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Domain.Shared.Base;

namespace Domain.Groups;

public class AlreadyMemberException : DomainException
{
public AlreadyMemberException(UserId user, Group group) : base($"The user with ID [{user.Value}] is alread member of group [{group.Name.Value} | {group.Id.Value}]")
{
}
}
5 changes: 5 additions & 0 deletions Domain/Groups/Events/GroupCreatedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Domain.Shared.Base;

namespace Domain.Groups;

public sealed class GroupCreatedEvent(Guid GroupId, string GroupName, Guid AdminId) : IDomainEvent;

Check warning on line 5 in Domain/Groups/Events/GroupCreatedEvent.cs

View workflow job for this annotation

GitHub Actions / Build project and run Architectural and Unit testing

Parameter 'GroupId' is unread.

Check warning on line 5 in Domain/Groups/Events/GroupCreatedEvent.cs

View workflow job for this annotation

GitHub Actions / Build project and run Architectural and Unit testing

Parameter 'GroupName' is unread.

Check warning on line 5 in Domain/Groups/Events/GroupCreatedEvent.cs

View workflow job for this annotation

GitHub Actions / Build project and run Architectural and Unit testing

Parameter 'AdminId' is unread.
5 changes: 5 additions & 0 deletions Domain/Groups/Events/MemberJoinedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Domain.Shared.Base;

namespace Domain.Groups;

public sealed class MemberJoinedEvent(Guid MemberId, Guid GroupId) : IDomainEvent;

Check warning on line 5 in Domain/Groups/Events/MemberJoinedEvent.cs

View workflow job for this annotation

GitHub Actions / Build project and run Architectural and Unit testing

Parameter 'MemberId' is unread.

Check warning on line 5 in Domain/Groups/Events/MemberJoinedEvent.cs

View workflow job for this annotation

GitHub Actions / Build project and run Architectural and Unit testing

Parameter 'GroupId' is unread.
8 changes: 8 additions & 0 deletions Domain/Groups/Repository/IGroupRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Domain.Shared.Base;

namespace Domain.Groups;

public interface IGroupRepository : IRepository<Group>
{
public Task Delete(Group group);
}
6 changes: 6 additions & 0 deletions Domain/Groups/ValueObjects/GroupId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Domain.Groups;

public record GroupId(Guid Value)
{
public static GroupId Create(Guid value) => new(value);
}
6 changes: 6 additions & 0 deletions Domain/Groups/ValueObjects/GroupName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Domain.Groups;

public record GroupName(string Value)
{
public static GroupName Create(string name) => new(name);
}
6 changes: 6 additions & 0 deletions Domain/Groups/ValueObjects/GroupRecords.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Domain.Groups;

public class GroupRecords
{

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Domain.Budgets;
using Domain;
using Domain.Budgets;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Newtonsoft.Json;
Expand All @@ -15,6 +16,11 @@ public void Configure(EntityTypeBuilder<Budget> builder)
.HasConversion(
src => src.Value,
raw => BudgetId.Create(raw));

builder.Property(budget => budget.Owner)
.HasConversion(
src => src.Value,
raw => UserId.Create(raw));

builder.ComplexProperty(budget => budget.MaximumAmount);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Domain.Expenses;
using Domain;
using Domain.Expenses;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Newtonsoft.Json;
Expand All @@ -21,6 +22,11 @@ public void Configure(EntityTypeBuilder<Expense> builder)
src => src.Value,
raw => ExpenseId.Create(raw));

builder.Property(expense => expense.User)
.HasConversion(
src => src.Value,
raw => UserId.Create(raw));

builder.ComplexProperty(expense => expense.Amount);

builder.Property(expense => expense.Description)
Expand Down
Loading

0 comments on commit 814cfc4

Please sign in to comment.