Skip to content

Commit

Permalink
Refactor entities and add group creation feature
Browse files Browse the repository at this point in the history
Refactored several entities to use GUIDs instead of strings for IDs, including updates to the `Certificate`, `Group`, and `Skill` classes. Added new properties and methods to support domain-driven design, such as the `CreateGroupCommandHandler` and `GroupCreated` domain event. Updated `ServiceCollectionExtensions` to register `IUnitOfWork` as a transient service. Introduced a new migration file to handle database schema changes. Added a new API endpoint for group creation in `GroupApi.cs`.
  • Loading branch information
PhucNghi176 committed Oct 2, 2024
1 parent 47ca9ae commit 813c610
Show file tree
Hide file tree
Showing 13 changed files with 1,028 additions and 19 deletions.
1 change: 0 additions & 1 deletion MBS_COMMAND.Application/MBS_COMMAND.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
</ItemGroup>

<ItemGroup>
<Folder Include="UserCases\Commands\" />
<Folder Include="UserCases\Queries\" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using MBS_COMMAND.Contract.Abstractions.Messages;
using MBS_COMMAND.Contract.Abstractions.Shared;
using MBS_COMMAND.Contract.Services.Groups;
using MBS_COMMAND.Domain.Abstractions.Repositories;
using MBS_COMMAND.Domain.Entities;

namespace MBS_COMMAND.Application.UserCases.Commands.Groups;

public sealed class CreateGroupCommandHandler : ICommandHandler<Command.CreateGroupCommand>
{
private readonly IRepositoryBase<Group, Guid> _repositoryBase;

public CreateGroupCommandHandler(IRepositoryBase<Group, Guid> repositoryBase)
{
_repositoryBase = repositoryBase;
}

public async Task<Result> Handle(Command.CreateGroupCommand request, CancellationToken cancellationToken)
{
var G = Group.Create(request.Name, request.Stacks, request.MentorId);
_repositoryBase.Add(G);
return Result.Success();
}
}
8 changes: 8 additions & 0 deletions MBS_COMMAND.Contract/Services/Groups/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MBS_COMMAND.Contract.Abstractions.Messages;

namespace MBS_COMMAND.Contract.Services.Groups;

public static class Command
{
public record CreateGroupCommand(string Name,Guid MentorId,string Stacks) : ICommand;
}
8 changes: 8 additions & 0 deletions MBS_COMMAND.Contract/Services/Groups/DomainEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MBS_COMMAND.Contract.Abstractions.Messages;

namespace MBS_COMMAND.Contract.Services.Groups;

public static class DomainEvents
{
public record GroupCreated(Guid IdEvent, Guid Id, string Name, Guid? MentorId, string Stacks) : IDomainEvent, ICommand;
}
4 changes: 2 additions & 2 deletions MBS_COMMAND.Domain/Entities/Certificate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ public class Certificate : Entity<Guid>,IAuditableEntity
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string SkillId { get; set; }
public virtual Skill Skill { get; set; }
public Guid SkillId { get; set; }
public virtual Skill? Skill { get; set; }
public DateTimeOffset CreatedOnUtc { get ; set ; }
public DateTimeOffset? ModifiedOnUtc { get ; set ; }
}
36 changes: 28 additions & 8 deletions MBS_COMMAND.Domain/Entities/Group.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
using MBS_AUTHORIZATION.Domain.Entities;
using MBS_COMMAND.Domain.Abstractions.Aggregates;
using MBS_COMMAND.Domain.Abstractions.Entities;

namespace MBS_COMMAND.Domain.Entities;

public class Group : Entity<Guid>, IAuditableEntity
public class Group : AggregateRoot<Guid>, IAuditableEntity
{
public string Name { get; set; }
public Guid? MentorId { get; set; }
public string Name { get; private set; }
public Guid? MentorId { get; private set; }
public virtual User? Mentor { get; set; }

public string? LeaderId { get; set; }
public Guid? LeaderId { get; set; }
public virtual User? Leader { get; set; }
public string Stack { get; set; }
public Guid? ProjectId { get; set; }
public string Stack { get; private set; }
public Guid? ProjectId { get; private set; }
public virtual Project? Project { get; set; }
public DateTimeOffset CreatedOnUtc { get ; set ; }
public DateTimeOffset? ModifiedOnUtc { get ; set ; }
public DateTimeOffset CreatedOnUtc { get; set; }
public DateTimeOffset? ModifiedOnUtc { get; set; }



public Group(string name, string stack, Guid? mentorId)
{
Name = name;
Stack = stack;
MentorId = mentorId;
}

public static Group Create(string name, string stack, Guid? mentorId)
{
var G = new Group(name, stack, mentorId);
G.RaiseDomainEvent(new Contract.Services.Groups.DomainEvents.GroupCreated(Guid.NewGuid(), G.Id, G.Name, G.MentorId, G.Stack));
return G;
}

}


4 changes: 4 additions & 0 deletions MBS_COMMAND.Domain/Entities/Skill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public class Skill : Entity<Guid>, IAuditableEntity
public virtual Category? Category { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string? CertificateId { get; set; }
public DateTimeOffset CreatedOnUtc { get; set; }
public DateTimeOffset? ModifiedOnUtc { get; set; }

public virtual IEnumerable<Certificate>? Certificates { get; set; } = [];

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

using MBS_COMMAND.Domain.Abstractions;
using MBS_COMMAND.Domain.Abstractions.Repositories;
using MBS_COMMAND.Persistence.DependencyInjection.Options;
using MBS_COMMAND.Persistence.Interceptors;
Expand Down Expand Up @@ -69,6 +70,7 @@ public static void AddInterceptorPersistence(this IServiceCollection services)
public static void AddRepositoryPersistence(this IServiceCollection services)
{
services.AddTransient(typeof(IRepositoryBase<,>), typeof(RepositoryBase<,>));
services.AddTransient(typeof(IUnitOfWork), typeof(EFUnitOfWork));
}

public static OptionsBuilder<SqlServerRetryOptions> ConfigureSqlServerRetryOptionsPersistence(this IServiceCollection services, IConfigurationSection section)
Expand Down
78 changes: 78 additions & 0 deletions MBS_COMMAND.Persistence/EFUnitOfWork.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using MBS_COMMAND.Domain.Abstractions;
using MBS_COMMAND.Persistence.Outbox;
using MBS_COMMAND.Domain.Abstractions.Entities;

namespace MBS_COMMAND.Persistence;

public class EFUnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _dbContext;

public EFUnitOfWork(ApplicationDbContext dbContext)
=> _dbContext = dbContext;

public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
{
//ConvertDomainEventsToOutboxMessages();
//UpdateAuditableEntities();
await _dbContext.SaveChangesAsync();
}

async ValueTask IAsyncDisposable.DisposeAsync()
=> await _dbContext.DisposeAsync();

private void ConvertDomainEventsToOutboxMessages()
{
var outboxMessages = _dbContext.ChangeTracker
.Entries<Domain.Abstractions.Aggregates.AggregateRoot<Guid>>()
.Select(x => x.Entity)
.SelectMany(aggregateRoot =>
{
var domainEvents = aggregateRoot.GetDomainEvents();

aggregateRoot.ClearDomainEvents();

return domainEvents;
})
.Select(domainEvent => new OutboxMessage
{
Id = Guid.NewGuid(),
OccurredOnUtc = DateTime.UtcNow,
Type = domainEvent.GetType().Name,
Content = JsonConvert.SerializeObject(
domainEvent,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
})
})
.ToList();

_dbContext.Set<OutboxMessage>().AddRange(outboxMessages);
}

private void UpdateAuditableEntities()
{
IEnumerable<EntityEntry<IAuditableEntity>> entries =
_dbContext
.ChangeTracker
.Entries<IAuditableEntity>();

foreach (EntityEntry<IAuditableEntity> entityEntry in entries)
{
if (entityEntry.State == EntityState.Added)
{
entityEntry.Property(a => a.CreatedOnUtc).CurrentValue = DateTime.UtcNow;
}

if (entityEntry.State == EntityState.Modified)
{
entityEntry.Property(a => a.ModifiedOnUtc).CurrentValue = DateTime.UtcNow;
}
}
}
}
Loading

0 comments on commit 813c610

Please sign in to comment.