From 72be8cc7b7a337b2a3d7a23b3a8ce03ad1176542 Mon Sep 17 00:00:00 2001 From: Ayush2395 Date: Tue, 8 Jul 2025 23:52:02 +0530 Subject: [PATCH] - Move the ApplicationUser entity from Infrastructure to Domain. - Added 2 interfaces IDomainEvent and IAuditableEntity in the Domain, I've putted this in common folder. - Change the Event from class to record. - In infrastructure class library, I've changed the interceptors with IDomainEvent and IAuditableEntity. - Added the IAuditableEntity and IDomainEvent in the ApplicationUser entity. - These changes helps to solve the complexity of project when it is scalling, that's I've experienced. --- Directory.Packages.props | 1 + src/Domain/Common/BaseAuditableEntity.cs | 2 +- src/Domain/Common/BaseEntity.cs | 2 +- src/Domain/Common/BaseEvent.cs | 4 +- src/Domain/Common/IAuditableEntity.cs | 11 + src/Domain/Common/IDomainEvent.cs | 11 + src/Domain/Domain.csproj | 1 + src/Domain/Entities/ApplicationUser.cs | 31 + src/Domain/Entities/TodoItem.cs | 2 +- src/Domain/Entities/TodoList.cs | 2 +- src/Domain/Events/TodoItemCompletedEvent.cs | 10 +- src/Domain/Events/TodoItemCreatedEvent.cs | 10 +- src/Domain/Events/TodoItemDeletedEvent.cs | 10 +- .../Data/ApplicationDbContext.cs | 1 - .../Data/ApplicationDbContextInitialiser.cs | 1 - .../AuditableEntityInterceptor.cs | 10 +- .../DispatchDomainEventsInterceptor.cs | 2 +- .../20250708180435_Refactor.Designer.cs | 411 +++++++++++++ .../Migrations/20250708180435_Refactor.cs | 61 ++ .../ApplicationDbContextModelSnapshot.cs | 550 +++++++++--------- src/Infrastructure/DependencyInjection.cs | 1 + .../Identity/ApplicationUser.cs | 7 - .../Identity/IdentityService.cs | 1 + src/Web/Pages/Shared/_LoginPartial.cshtml | 3 +- 24 files changed, 818 insertions(+), 327 deletions(-) create mode 100644 src/Domain/Common/IAuditableEntity.cs create mode 100644 src/Domain/Common/IDomainEvent.cs create mode 100644 src/Domain/Entities/ApplicationUser.cs create mode 100644 src/Infrastructure/Data/Migrations/20250708180435_Refactor.Designer.cs create mode 100644 src/Infrastructure/Data/Migrations/20250708180435_Refactor.cs delete mode 100644 src/Infrastructure/Identity/ApplicationUser.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index dd66fcb3b..98436fb27 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,6 +19,7 @@ + diff --git a/src/Domain/Common/BaseAuditableEntity.cs b/src/Domain/Common/BaseAuditableEntity.cs index 397e15b71..acffbf506 100644 --- a/src/Domain/Common/BaseAuditableEntity.cs +++ b/src/Domain/Common/BaseAuditableEntity.cs @@ -1,6 +1,6 @@ namespace CleanArchitecture.Domain.Common; -public abstract class BaseAuditableEntity : BaseEntity +public abstract class BaseAuditableEntity : IAuditableEntity { public DateTimeOffset Created { get; set; } diff --git a/src/Domain/Common/BaseEntity.cs b/src/Domain/Common/BaseEntity.cs index f7d1f0379..ae44b1467 100644 --- a/src/Domain/Common/BaseEntity.cs +++ b/src/Domain/Common/BaseEntity.cs @@ -2,7 +2,7 @@ namespace CleanArchitecture.Domain.Common; -public abstract class BaseEntity +public abstract class BaseEntity : BaseAuditableEntity, IDomainEvent { // This can easily be modified to be BaseEntity and public T Id to support different key types. // Using non-generic integer types for simplicity diff --git a/src/Domain/Common/BaseEvent.cs b/src/Domain/Common/BaseEvent.cs index aa9ee5a26..3a8a53017 100644 --- a/src/Domain/Common/BaseEvent.cs +++ b/src/Domain/Common/BaseEvent.cs @@ -2,6 +2,4 @@ namespace CleanArchitecture.Domain.Common; -public abstract class BaseEvent : INotification -{ -} +public record BaseEvent : INotification; diff --git a/src/Domain/Common/IAuditableEntity.cs b/src/Domain/Common/IAuditableEntity.cs new file mode 100644 index 000000000..8ff187ac7 --- /dev/null +++ b/src/Domain/Common/IAuditableEntity.cs @@ -0,0 +1,11 @@ +namespace CleanArchitecture.Domain.Common; +public interface IAuditableEntity +{ + DateTimeOffset Created { get; set; } + + string? CreatedBy { get; set; } + + DateTimeOffset LastModified { get; set; } + + string? LastModifiedBy { get; set; } +} diff --git a/src/Domain/Common/IDomainEvent.cs b/src/Domain/Common/IDomainEvent.cs new file mode 100644 index 000000000..28870e308 --- /dev/null +++ b/src/Domain/Common/IDomainEvent.cs @@ -0,0 +1,11 @@ +namespace CleanArchitecture.Domain.Common; +public interface IDomainEvent +{ + IReadOnlyCollection DomainEvents { get; } + + void AddDomainEvent(BaseEvent domainEvent); + + void RemoveDomainEvent(BaseEvent domainEvent); + + void ClearDomainEvents(); +} diff --git a/src/Domain/Domain.csproj b/src/Domain/Domain.csproj index f88e11f3c..1d84acb86 100644 --- a/src/Domain/Domain.csproj +++ b/src/Domain/Domain.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Domain/Entities/ApplicationUser.cs b/src/Domain/Entities/ApplicationUser.cs new file mode 100644 index 000000000..edb87804b --- /dev/null +++ b/src/Domain/Entities/ApplicationUser.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.AspNetCore.Identity; + +namespace CleanArchitecture.Domain.Entities; +public class ApplicationUser : IdentityUser, IAuditableEntity, IDomainEvent +{ + public DateTimeOffset Created { get; set; } + public string? CreatedBy { get; set; } + public DateTimeOffset LastModified { get; set; } + public string? LastModifiedBy { get; set; } + + private readonly List _domainEvents = []; + + [NotMapped] + public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly(); + + public void AddDomainEvent(BaseEvent domainEvent) + { + _domainEvents.Add(domainEvent); + } + + public void ClearDomainEvents() + { + _domainEvents.Clear(); + } + + public void RemoveDomainEvent(BaseEvent domainEvent) + { + _domainEvents.Remove(domainEvent); + } +} diff --git a/src/Domain/Entities/TodoItem.cs b/src/Domain/Entities/TodoItem.cs index 683ea0c31..857357793 100644 --- a/src/Domain/Entities/TodoItem.cs +++ b/src/Domain/Entities/TodoItem.cs @@ -1,6 +1,6 @@ namespace CleanArchitecture.Domain.Entities; -public class TodoItem : BaseAuditableEntity +public class TodoItem : BaseEntity { public int ListId { get; set; } diff --git a/src/Domain/Entities/TodoList.cs b/src/Domain/Entities/TodoList.cs index 6da36445b..8fc8a274a 100644 --- a/src/Domain/Entities/TodoList.cs +++ b/src/Domain/Entities/TodoList.cs @@ -1,6 +1,6 @@ namespace CleanArchitecture.Domain.Entities; -public class TodoList : BaseAuditableEntity +public class TodoList : BaseEntity { public string? Title { get; set; } diff --git a/src/Domain/Events/TodoItemCompletedEvent.cs b/src/Domain/Events/TodoItemCompletedEvent.cs index 6324737be..9dd283275 100644 --- a/src/Domain/Events/TodoItemCompletedEvent.cs +++ b/src/Domain/Events/TodoItemCompletedEvent.cs @@ -1,11 +1,3 @@ namespace CleanArchitecture.Domain.Events; -public class TodoItemCompletedEvent : BaseEvent -{ - public TodoItemCompletedEvent(TodoItem item) - { - Item = item; - } - - public TodoItem Item { get; } -} +public record TodoItemCompletedEvent(TodoItem Item) : BaseEvent; diff --git a/src/Domain/Events/TodoItemCreatedEvent.cs b/src/Domain/Events/TodoItemCreatedEvent.cs index 76c2083f6..f65c7f800 100644 --- a/src/Domain/Events/TodoItemCreatedEvent.cs +++ b/src/Domain/Events/TodoItemCreatedEvent.cs @@ -1,11 +1,3 @@ namespace CleanArchitecture.Domain.Events; -public class TodoItemCreatedEvent : BaseEvent -{ - public TodoItemCreatedEvent(TodoItem item) - { - Item = item; - } - - public TodoItem Item { get; } -} +public record TodoItemCreatedEvent(TodoItem Item) : BaseEvent; diff --git a/src/Domain/Events/TodoItemDeletedEvent.cs b/src/Domain/Events/TodoItemDeletedEvent.cs index 64d5bbb64..055439389 100644 --- a/src/Domain/Events/TodoItemDeletedEvent.cs +++ b/src/Domain/Events/TodoItemDeletedEvent.cs @@ -1,11 +1,3 @@ namespace CleanArchitecture.Domain.Events; -public class TodoItemDeletedEvent : BaseEvent -{ - public TodoItemDeletedEvent(TodoItem item) - { - Item = item; - } - - public TodoItem Item { get; } -} +public record TodoItemDeletedEvent(TodoItem Item) : BaseEvent; diff --git a/src/Infrastructure/Data/ApplicationDbContext.cs b/src/Infrastructure/Data/ApplicationDbContext.cs index 580b3a633..27bad3b48 100644 --- a/src/Infrastructure/Data/ApplicationDbContext.cs +++ b/src/Infrastructure/Data/ApplicationDbContext.cs @@ -1,7 +1,6 @@ using System.Reflection; using CleanArchitecture.Application.Common.Interfaces; using CleanArchitecture.Domain.Entities; -using CleanArchitecture.Infrastructure.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; diff --git a/src/Infrastructure/Data/ApplicationDbContextInitialiser.cs b/src/Infrastructure/Data/ApplicationDbContextInitialiser.cs index 75e01bc2c..04a20ffd5 100644 --- a/src/Infrastructure/Data/ApplicationDbContextInitialiser.cs +++ b/src/Infrastructure/Data/ApplicationDbContextInitialiser.cs @@ -1,6 +1,5 @@ using CleanArchitecture.Domain.Constants; using CleanArchitecture.Domain.Entities; -using CleanArchitecture.Infrastructure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; diff --git a/src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs b/src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs index 3d584516c..443061f3d 100644 --- a/src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs +++ b/src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs @@ -37,7 +37,7 @@ public void UpdateEntities(DbContext? context) { if (context == null) return; - foreach (var entry in context.ChangeTracker.Entries()) + foreach (var entry in context.ChangeTracker.Entries()) { if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities()) { @@ -46,7 +46,7 @@ public void UpdateEntities(DbContext? context) { entry.Entity.CreatedBy = _user.Id; entry.Entity.Created = utcNow; - } + } entry.Entity.LastModifiedBy = _user.Id; entry.Entity.LastModified = utcNow; } @@ -57,8 +57,8 @@ public void UpdateEntities(DbContext? context) public static class Extensions { public static bool HasChangedOwnedEntities(this EntityEntry entry) => - entry.References.Any(r => - r.TargetEntry != null && - r.TargetEntry.Metadata.IsOwned() && + entry.References.Any(r => + r.TargetEntry != null && + r.TargetEntry.Metadata.IsOwned() && (r.TargetEntry.State == EntityState.Added || r.TargetEntry.State == EntityState.Modified)); } diff --git a/src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs b/src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs index 9f25fc0c4..39f4ba0a2 100644 --- a/src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs +++ b/src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs @@ -34,7 +34,7 @@ public async Task DispatchDomainEvents(DbContext? context) if (context == null) return; var entities = context.ChangeTracker - .Entries() + .Entries() .Where(e => e.Entity.DomainEvents.Any()) .Select(e => e.Entity); diff --git a/src/Infrastructure/Data/Migrations/20250708180435_Refactor.Designer.cs b/src/Infrastructure/Data/Migrations/20250708180435_Refactor.Designer.cs new file mode 100644 index 000000000..0a40460d2 --- /dev/null +++ b/src/Infrastructure/Data/Migrations/20250708180435_Refactor.Designer.cs @@ -0,0 +1,411 @@ +// +using System; +using CleanArchitecture.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace CleanArchitecture.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250708180435_Refactor")] + partial class Refactor + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Done") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ListId") + .HasColumnType("int"); + + b.Property("Note") + .HasColumnType("nvarchar(max)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Reminder") + .HasColumnType("datetime2"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("ListId"); + + b.ToTable("TodoItems"); + }); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.ToTable("TodoLists"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoItem", b => + { + b.HasOne("CleanArchitecture.Domain.Entities.TodoList", "List") + .WithMany("Items") + .HasForeignKey("ListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("List"); + }); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => + { + b.OwnsOne("CleanArchitecture.Domain.ValueObjects.Colour", "Colour", b1 => + { + b1.Property("TodoListId") + .HasColumnType("int"); + + b1.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("TodoListId"); + + b1.ToTable("TodoLists"); + + b1.WithOwner() + .HasForeignKey("TodoListId"); + }); + + b.Navigation("Colour") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Data/Migrations/20250708180435_Refactor.cs b/src/Infrastructure/Data/Migrations/20250708180435_Refactor.cs new file mode 100644 index 000000000..11f3c4d83 --- /dev/null +++ b/src/Infrastructure/Data/Migrations/20250708180435_Refactor.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace CleanArchitecture.Infrastructure.Data.Migrations +{ + /// + public partial class Refactor : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Created", + table: "AspNetUsers", + type: "datetimeoffset", + nullable: false, + defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0))); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastModified", + table: "AspNetUsers", + type: "datetimeoffset", + nullable: false, + defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0))); + + migrationBuilder.AddColumn( + name: "LastModifiedBy", + table: "AspNetUsers", + type: "nvarchar(max)", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Created", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "LastModified", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "LastModifiedBy", + table: "AspNetUsers"); + } + } +} diff --git a/src/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 5e6d67a7d..4951c6765 100644 --- a/src/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -22,390 +22,386 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + modelBuilder.Entity("CleanArchitecture.Domain.Entities.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); - b.Property("Created") - .HasColumnType("datetimeoffset"); + b.Property("Created") + .HasColumnType("datetimeoffset"); - b.Property("CreatedBy") - .HasColumnType("nvarchar(max)"); + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); - b.Property("Done") - .HasColumnType("bit"); + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - b.Property("LastModified") - .HasColumnType("datetimeoffset"); + b.Property("EmailConfirmed") + .HasColumnType("bit"); - b.Property("LastModifiedBy") - .HasColumnType("nvarchar(max)"); + b.Property("LastModified") + .HasColumnType("datetimeoffset"); - b.Property("ListId") - .HasColumnType("int"); + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); - b.Property("Note") - .HasColumnType("nvarchar(max)"); + b.Property("LockoutEnabled") + .HasColumnType("bit"); - b.Property("Priority") - .HasColumnType("int"); + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); - b.Property("Reminder") - .HasColumnType("datetime2"); + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - b.Property("Title") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); - b.HasIndex("ListId"); + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); - b.ToTable("TodoItems"); - }); + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); - modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.HasKey("Id"); - b.Property("Created") - .HasColumnType("datetimeoffset"); + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); - b.Property("CreatedBy") - .HasColumnType("nvarchar(max)"); + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); - b.Property("LastModified") - .HasColumnType("datetimeoffset"); + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); - b.Property("LastModifiedBy") - .HasColumnType("nvarchar(max)"); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("Title") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); + b.Property("Created") + .HasColumnType("datetimeoffset"); - b.HasKey("Id"); + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); - b.ToTable("TodoLists"); - }); + b.Property("Done") + .HasColumnType("bit"); - modelBuilder.Entity("CleanArchitecture.Infrastructure.Identity.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); + b.Property("LastModified") + .HasColumnType("datetimeoffset"); - b.Property("AccessFailedCount") - .HasColumnType("int"); + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); + b.Property("ListId") + .HasColumnType("int"); - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.Property("Note") + .HasColumnType("nvarchar(max)"); - b.Property("EmailConfirmed") - .HasColumnType("bit"); + b.Property("Priority") + .HasColumnType("int"); - b.Property("LockoutEnabled") - .HasColumnType("bit"); + b.Property("Reminder") + .HasColumnType("datetime2"); - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.HasKey("Id"); - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.HasIndex("ListId"); - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); + b.ToTable("TodoItems"); + }); - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); + modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); + b.Property("Created") + .HasColumnType("datetimeoffset"); - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.Property("LastModified") + .HasColumnType("datetimeoffset"); - b.HasKey("Id"); + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); + b.HasKey("Id"); - b.ToTable("AspNetUsers", (string)null); - }); + b.ToTable("TodoLists"); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles", (string)null); - }); + b.ToTable("AspNetRoles", (string)null); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("RoleId"); + b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims", (string)null); - }); + b.ToTable("AspNetRoleClaims", (string)null); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("UserId"); + b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims", (string)null); - }); + b.ToTable("AspNetUserClaims", (string)null); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") -#if (UseApiOnly && UseSqlServer) - .HasColumnType("nvarchar(450)"); -#else - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); -#endif + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); - b.Property("ProviderKey") -#if (UseApiOnly && UseSqlServer) - .HasColumnType("nvarchar(450)"); -#else - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); -#endif + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); - b.HasKey("LoginProvider", "ProviderKey"); + b.HasKey("LoginProvider", "ProviderKey"); - b.HasIndex("UserId"); + b.HasIndex("UserId"); - b.ToTable("AspNetUserLogins", (string)null); - }); + b.ToTable("AspNetUserLogins", (string)null); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); - b.HasKey("UserId", "RoleId"); + b.HasKey("UserId", "RoleId"); - b.HasIndex("RoleId"); + b.HasIndex("RoleId"); - b.ToTable("AspNetUserRoles", (string)null); - }); + b.ToTable("AspNetUserRoles", (string)null); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") -#if (UseApiOnly && UseSqlServer) - .HasColumnType("nvarchar(450)"); -#else - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); -#endif - - b.Property("Name") -#if (UseApiOnly && UseSqlServer) - .HasColumnType("nvarchar(450)"); -#else - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); -#endif - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoItem", b => - { - b.HasOne("CleanArchitecture.Domain.Entities.TodoList", "List") - .WithMany("Items") - .HasForeignKey("ListId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + { + b.HasOne("CleanArchitecture.Domain.Entities.TodoList", "List") + .WithMany("Items") + .HasForeignKey("ListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b.Navigation("List"); - }); + b.Navigation("List"); + }); modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => - { - b.OwnsOne("CleanArchitecture.Domain.ValueObjects.Colour", "Colour", b1 => { - b1.Property("TodoListId") - .HasColumnType("int"); + b.OwnsOne("CleanArchitecture.Domain.ValueObjects.Colour", "Colour", b1 => + { + b1.Property("TodoListId") + .HasColumnType("int"); - b1.Property("Code") - .IsRequired() - .HasColumnType("nvarchar(max)"); + b1.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); - b1.HasKey("TodoListId"); + b1.HasKey("TodoListId"); - b1.ToTable("TodoLists"); + b1.ToTable("TodoLists"); - b1.WithOwner() - .HasForeignKey("TodoListId"); - }); + b1.WithOwner() + .HasForeignKey("TodoListId"); + }); - b.Navigation("Colour") - .IsRequired(); - }); + b.Navigation("Colour") + .IsRequired(); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("CleanArchitecture.Infrastructure.Identity.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("CleanArchitecture.Infrastructure.Identity.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("CleanArchitecture.Infrastructure.Identity.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("CleanArchitecture.Infrastructure.Identity.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("CleanArchitecture.Domain.Entities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("CleanArchitecture.Domain.Entities.TodoList", b => - { - b.Navigation("Items"); - }); + { + b.Navigation("Items"); + }); #pragma warning restore 612, 618 } } diff --git a/src/Infrastructure/DependencyInjection.cs b/src/Infrastructure/DependencyInjection.cs index ecd89851a..cbec2e21d 100644 --- a/src/Infrastructure/DependencyInjection.cs +++ b/src/Infrastructure/DependencyInjection.cs @@ -1,5 +1,6 @@ using CleanArchitecture.Application.Common.Interfaces; using CleanArchitecture.Domain.Constants; +using CleanArchitecture.Domain.Entities; using CleanArchitecture.Infrastructure.Data; using CleanArchitecture.Infrastructure.Data.Interceptors; using CleanArchitecture.Infrastructure.Identity; diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs deleted file mode 100644 index 87b664e6b..000000000 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace CleanArchitecture.Infrastructure.Identity; - -public class ApplicationUser : IdentityUser -{ -} diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index 8fa38f4a8..80c4e3c91 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -1,5 +1,6 @@ using CleanArchitecture.Application.Common.Interfaces; using CleanArchitecture.Application.Common.Models; +using CleanArchitecture.Domain.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; diff --git a/src/Web/Pages/Shared/_LoginPartial.cshtml b/src/Web/Pages/Shared/_LoginPartial.cshtml index fb3d2e4dc..294956bf3 100644 --- a/src/Web/Pages/Shared/_LoginPartial.cshtml +++ b/src/Web/Pages/Shared/_LoginPartial.cshtml @@ -1,4 +1,5 @@ -@using CleanArchitecture.Infrastructure.Identity +@using CleanArchitecture.Domain.Entities +@using CleanArchitecture.Infrastructure.Identity @using Microsoft.AspNetCore.Identity @inject SignInManager SignInManager @inject UserManager UserManager