Infraestrutura - Yago-Captain/GeneralNotes GitHub Wiki

MyGeneralNotes.Infrastructure

Nesta seção, será explorado o diretório de Infraestrutura da aplicação, localizado dentro da pasta 'back' Aqui estão todas as implementações das interfaces definidas no domínio, bem como a camada de acesso a dados da aplicação. A infraestrutura é responsável por fornecer suporte técnico para a execução da lógica de negócios da aplicação, incluindo acesso a bancos de dados, serviços externos e outros recursos.

Camada de Acesso a Dados

A camada de Infraestrutura inclui também os componentes responsáveis pelo acesso a dados da aplicação. Isso inclui classes de contexto de banco de dados, repositórios migrations, serviços de persistência, entre outros.

Esses componentes são essenciais para garantir que os dados sejam armazenados e recuperados de maneira eficiente e segura. Eles são projetados para encapsular a complexidade do acesso a dados e fornecer uma interface limpa para a camada de negócios da aplicação.

Exploraremos em detalhes as diferentes partes da infraestrutura da nossa aplicação, incluindo como elas se integram com o restante do sistema e como são utilizadas para garantir o funcionamento adequado da aplicação.

Agora seguiremos com as classes responsáveis pelo acesso ao banco de dados da nossa aplicação:

MyGeneralNotesDbContext

using Microsoft.EntityFrameworkCore;
using MyGeneralNotes.Domain.Entities;

namespace MyGeneralNotes.Infrastructure.DataAccess
{
    public class MyGeneralNotesDbContext : DbContext
    {
        public MyGeneralNotesDbContext(DbContextOptions<MyGeneralNotesDbContext> options) : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
        public DbSet<Routine> Routines { get; set; }
        public DbSet<Exercise> Exercises { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(MyGeneralNotesDbContext).Assembly);
        }
    }
}

A classe MyGeneralNotesDbContext é a classe de contexto do Entity Framework Core. Ela herda de DbContext e contém DbSet para cada entidade (User, Routine, Exercise).

Essa classe é essencial para interagir com o banco de dados, pois fornece acesso aos conjuntos de entidades e gerencia as operações de banco de dados, como inserção, atualização, exclusão e consulta.

A classe de contexto é fundamental em aplicações que utilizam o Entity Framework Core, pois é responsável por várias tarefas importantes:

  • Fornece acesso aos conjuntos de entidades, representados pelos DbSet<T>, permitindo que você consulte e manipule os dados do banco de dados.
  • Gerencia o rastreamento de entidades, controlando o estado das entidades durante a vida útil de uma solicitação.
  • Coordena as operações de banco de dados, como transações, garantindo a consistência dos dados.
  • Mapeia entidades para tabelas no banco de dados e vice-versa, permitindo que você trabalhe com objetos no código enquanto armazena e recupera dados no banco de dados.

Em resumo, a classe de contexto é o ponto central para interagir com o banco de dados em uma aplicação usando o Entity Framework Core. Ela facilita o acesso e a manipulação de dados, proporcionando uma abstração sobre as operações de banco de dados subjacentes.

ConfigurationExtensions

using Microsoft.Extensions.Configuration;

namespace MyGeneralNotes.Infrastructure.Extensions
{
    public static class ConfigurationExtensions
    {
        public static string ConnectionString(this IConfiguration configuration)
        {
            return configuration.GetConnectionString("ConnectionMySql")!;
        }
    }
}

Esta classe estática contém um método de extensão para a interface IConfiguration chamado ConnectionString. Este método retorna a string de conexão do banco de dados que é definida no arquivo de configuração do aplicativo.

DatabaseMigration

using Dapper;
using FluentMigrator.Runner;
using Microsoft.Extensions.DependencyInjection;
using MySqlConnector;

namespace MyGeneralNotes.Infrastructure.Migrations
{
    public class DatabaseMigration
    {
        public static void Migrate(string connectionString, IServiceProvider serviceProvider)
        {
            DatabaseCreatedMySql(connectionString);
            MigrationDatabase(serviceProvider);
        }

        private static void DatabaseCreatedMySql(string connectionString)
        {
            var connectionStringBuilder = new MySqlConnectionStringBuilder(connectionString);

            var databaseName = connectionStringBuilder.Database;

            connectionStringBuilder.Remove("Database");

            using var dbConnection = new MySqlConnection(connectionStringBuilder.ConnectionString);

            var parameters = new DynamicParameters();
            parameters.Add("name", databaseName);

            var records = dbConnection.Query("SELECT * FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @name", parameters);

            if (records.Any() == false)
            {
                dbConnection.Execute($"CREATE DATABASE {databaseName}");
            }
        }

        private static void MigrationDatabase(IServiceProvider serviceProvider)
        {
            var runner = serviceProvider.GetRequiredService<IMigrationRunner>();

            runner.ListMigrations();

            runner.MigrateUp();
        }
    }
}

Esta classe estática contém métodos para criar o banco de dados (se ainda não existir) e executar migrações. O método Migrate é o ponto de entrada que chama os métodos DatabaseCreatedMySql e MigrationDatabase. O método DatabaseCreatedMySql verifica se o banco de dados já existe e, se não existir, cria um novo banco de dados. O método MigrationDatabase obtém uma instância do IMigrationRunner do contêiner de injeção de dependência e executa todas as migrações pendentes.

DataVersions

namespace MyGeneralNotes.Infrastructure.Migrations
{
    public abstract class DataVersions
    {
        public const int TABLE_USER = 1;
        public const int TABLE_ROUTINE_AND_EXERCISE = 2;
    }
}

Esta classe abstrata contém constantes que representam as versões das migrações de banco de dados. Cada constante é usada como um identificador para uma migração específica.

VersionBase

using FluentMigrator;
using FluentMigrator.Builders.Create.Table;

namespace MyGeneralNotes.Infrastructure.Migrations.Versions
{
    public abstract class VersionBase : ForwardOnlyMigration
    {
        protected ICreateTableColumnOptionOrWithColumnSyntax CreateTable(string table)
        {
            return Create.Table(table)
                .WithColumn("Id").AsInt64().PrimaryKey().Identity()
                .WithColumn("CreatedOn").AsDateTime().NotNullable()
                .WithColumn("Active").AsBoolean().NotNullable();
        }
    }
}

Esta é a classe base para todas as migrações. Ela herda de ForwardOnlyMigration e contém um método CreateTable que define colunas comuns a todas as tabelas. Este método é chamado em cada migração para iniciar a criação da tabela.

Version001 e Version002

Version001

using FluentMigrator;

namespace MyGeneralNotes.Infrastructure.Migrations.Versions
{
    [Migration(DataVersions.TABLE_USER, "Cria a tabela de Usuários")]
    public class Version001 : VersionBase
    {
        public override void Up()
        {
            CreateTable("Users")
                .WithColumn("Name").AsString(100).NotNullable()
                .WithColumn("Email").AsString(100).NotNullable()
                .WithColumn("Password").AsString(2000).NotNullable()
                .WithColumn("UserIdentifier").AsGuid().NotNullable();
        }
    }
}

Version002

using FluentMigrator;

namespace MyGeneralNotes.Infrastructure.Migrations.Versions
{
    [Migration(DataVersions.TABLE_ROUTINE_AND_EXERCISE, "Cria as tabelas de Rotinas e Exercícios")]
    public class Version002 : VersionBase
    {
        public override void Up()
        {
            CreateTableRoutine();
            CreateTableExercice();
        }

        private void CreateTableRoutine()
        {
            CreateTable("Routines")
                .WithColumn("Name").AsString(100).NotNullable()
                .WithColumn("DayOfWeek").AsInt16().NotNullable()
                .WithColumn("UserId").AsInt64().NotNullable().ForeignKey("FK_Routine_User_Id", "Users", "Id");
        }

        private void CreateTableExercice()
        {
            CreateTable("Exercises")
                .WithColumn("Name").AsString(100).NotNullable()
                .WithColumn("Location").AsInt32().NotNullable()
                .WithColumn("Charge").AsDouble().NotNullable()
                .WithColumn("Repetitions").AsInt32().NotNullable()
                .WithColumn("RestTime").AsInt32().NotNullable()
                .WithColumn("Equipment").AsString(100).NotNullable()
                .WithColumn("Details").AsString(100).Nullable()
                .WithColumn("RoutineId").AsInt64().NotNullable().ForeignKey("FK_Exercise_Routine_Id", "Routines", "Id").OnDeleteOrUpdate(System.Data.Rule.Cascade);
        }
    }
}

Essas classes representam migrações específicas para criar a tabela de usuários e as tabelas de rotinas e exercícios, respectivamente. Elas herdam de VersionBase e sobrescrevem o método Up para definir a criação das tabelas. A anotação [Migration] acima de cada classe indica que são migrações, e o número passado para a anotação é a versão da migração.

Implementações das Interfaces do Domínio

As interfaces definidas no diretório de Domínio são implementadas na camada de Infraestrutura. Cada interface define um contrato que especifica as operações que devem ser realizadas, enquanto as implementações fornecem a lógica concreta para executar essas operações.

Por exemplo, as interfaces IUserReadOnlyRepository, IRoutineWriteOnlyRepository, entre outras, têm suas implementações correspondentes neste diretório. Essas implementações interagem diretamente com o banco de dados ou outros sistemas externos para realizar as operações necessárias.

UserRepository

using Microsoft.EntityFrameworkCore;
using MyGeneralNotes.Domain.Entities;
using MyGeneralNotes.Domain.Repositories.User;

namespace MyGeneralNotes.Infrastructure.DataAccess.Repositories
{
    public class UserRepository : IUserWriteOnlyRepository, IUserReadOnlyRepository, IUserUpdateOnlyRepository
    {
        private readonly MyGeneralNotesDbContext _context;

        public UserRepository(MyGeneralNotesDbContext context)
        {
            _context = context;
        }

        public async Task Add(User user) => await _context.Users.AddAsync(user);

        public async Task<bool> ExistActiveUserWithEmail(string email) =>
            await _context.Users.AnyAsync(user => user.Email.Equals(email) && user.Active);

        public async Task<User?> GetUserByEmailAndPassword(string email, string password) =>
            await _context.Users.AsNoTracking().FirstOrDefaultAsync(user => user.Active && user.Email.Equals(email) && user.Password.Equals(password));

        public async Task<bool> ExistActiveUserWitchIdentifier(Guid userIdentifier) =>
            await _context.Users.AnyAsync(user => user.UserIdentifier.Equals(userIdentifier) && user.Active);

        public async Task<User> GetByUserIdentifier(Guid userIdentifier) =>
            await _context.Users.AsNoTracking().FirstAsync(user => user.Active && user.UserIdentifier.Equals(userIdentifier));

        public async Task<User> GetById(long id) =>
            await _context.Users.FirstAsync(user => user.Id == id);

        public void Update(User user) => _context.Users.Update(user);
    }
}

A classe UserRepository implementa as interfaces IUserWriteOnlyRepository, IUserReadOnlyRepository e IUserUpdateOnlyRepository. A classe tem uma dependência no MyGeneralNotesDbContext, que é injetada através do construtor e armazenada em um campo privado _context.

Aqui estão os métodos implementados na classe UserRepository:

  1. Add(User user): Este método assíncrono adiciona um novo usuário ao contexto do banco de dados. Ele usa o método AddAsync do DbSet<User> no _context.

  2. ExistActiveUserWithEmail(string email): Este método assíncrono verifica se existe um usuário ativo com o e-mail fornecido no contexto do banco de dados. Ele usa o método AnyAsync do DbSet<User> no _context.

  3. GetUserByEmailAndPassword(string email, string password): Este método assíncrono recupera um usuário ativo com o e-mail e a senha fornecidos do contexto do banco de dados. Ele usa o método FirstOrDefaultAsync do DbSet<User> no _context.

  4. ExistActiveUserWitchIdentifier(Guid userIdentifier): Este método assíncrono verifica se existe um usuário ativo com o identificador de usuário fornecido no contexto do banco de dados. Ele usa o método AnyAsync do DbSet<User> no _context.

  5. GetByUserIdentifier(Guid userIdentifier): Este método assíncrono recupera um usuário ativo com o identificador de usuário fornecido do contexto do banco de dados. Ele usa o método FirstAsync do DbSet<User> no _context.

  6. GetById(long id): Este método assíncrono recupera um usuário com o ID fornecido do contexto do banco de dados. Ele usa o método FirstAsync do DbSet<User> no _context.

  7. Update(User user): Este método atualiza um usuário existente no contexto do banco de dados. Ele usa o método Update do DbSet<User> no _context.

RoutineRepository

using Microsoft.EntityFrameworkCore;
using MyGeneralNotes.Domain.Entities;
using MyGeneralNotes.Domain.Repositories.Routine;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyGeneralNotes.Infrastructure.DataAccess.Repositories
{
    public class RoutineRepository : IRoutineWriteOnlyRepository, IRoutineReadOnlyRepository, IRoutineUpdateOnlyRepository
    {
        private readonly MyGeneralNotesDbContext _context;

        public RoutineRepository(MyGeneralNotesDbContext context)
        {
            _context = context;
        }

        public async Task Delete(long routineId)
        {
            var routine = await _context.Routines.FirstOrDefaultAsync(r => r.Id == routineId);
            if (routine != null)
                _context.Routines.Remove(routine);
        }

        public async Task<IList<Routine>> GetAllUserRoutines(long userId)
        {
            return await _context.Routines
                .AsNoTracking()
                .Include(ex => ex.Exercises)
                .Where(r => r.UserId == userId).ToListAsync();
        }

        public async Task<Routine?> GetById(long routineId)
        {
            return await _context.Routines
                 .Include(ex => ex.Exercises)
                 .FirstOrDefaultAsync(r => r.Id == routineId);
        }

        public async Task<Routine?> GetRoutine(long routineId)
        {
            return await _context.Routines
                .AsNoTracking()
                .Include(ex => ex.Exercises)
                .FirstOrDefaultAsync(r => r.Id == routineId);
        }

        public async Task Register(Routine routine) => await _context.Routines.AddAsync(routine);

        public void Update(Routine routine) => _context.Routines.Update(routine);
    }
}

A classe RoutineRepository implementa as interfaces IRoutineWriteOnlyRepository, IRoutineReadOnlyRepository e IRoutineUpdateOnlyRepository. A classe tem uma dependência no MyGeneralNotesDbContext, que é injetada através do construtor e armazenada em um campo privado _context.

Aqui estão os métodos implementados na classe RoutineRepository:

  1. Delete(long routineId): Este método assíncrono exclui uma rotina do contexto do banco de dados se ela existir. Ele primeiro busca a rotina pelo ID fornecido e, se encontrada, a remove do DbSet<Routines> no _context.
  2. GetAllUserRoutines(long userId): Este método assíncrono recupera todas as rotinas de um usuário do contexto do banco de dados. Ele retorna uma lista de rotinas para o ID de usuário fornecido, incluindo os exercícios associados a cada rotina.
  3. GetById(long routineId): Este método assíncrono recupera uma rotina do contexto do banco de dados pelo ID fornecido, incluindo os exercícios associados à rotina.
  4. GetRoutine(long routineId): Este método assíncrono recupera uma rotina do contexto do banco de dados pelo ID fornecido, incluindo os exercícios associados à rotina.
  5. Register(Routine routine): Este método assíncrono adiciona uma nova rotina ao contexto do banco de dados. Ele usa o método AddAsync do DbSet<Routines> no _context.
  6. Update(Routine routine): Este método atualiza uma rotina existente no contexto do banco de dados. Ele usa o método Update do DbSet<Routines> no _context.

Sha512Encripter

using MyGeneralNotes.Domain.Security.Cryptography;
using System.Security.Cryptography;
using System.Text;

namespace MyGeneralNotes.Infrastructure.Security.Cryptography
{
    public class Sha512Encripter : IPasswordEncripter
    {
        private readonly string _additionalKey;

        public Sha512Encripter(string additionalKey)
        {
            _additionalKey = additionalKey;
        }

        public string Encrypt(string password)
        {
            var newPassword = $"{password}{_additionalKey}";

            var bytes = Encoding.UTF8.GetBytes(newPassword);
            var hashBytes = SHA512.HashData(bytes);

            return StringBytes(hashBytes);
        }

        private static string StringBytes(byte[] bytes)
        {
            var sb = new StringBuilder();
            foreach (var b in bytes)
            {
                var hex = b.ToString("x2");
                sb.Append(hex);
            }
            return sb.ToString();
        }
    }
}

Esta classe implementa a interface IPasswordEncripter e é usada para criptografar senhas usando o algoritmo SHA512. Ela tem um campo privado _additionalKey que é usado como uma chave adicional durante a criptografia. O método Encrypt concatena a senha fornecida com a chave adicional, converte a nova senha em bytes, calcula o hash SHA512 e retorna a string hexadecimal do hash.

JwtTokenGenerator

using Microsoft.IdentityModel.Tokens;
using MyGeneralNotes.Domain.Security.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace MyGeneralNotes.Infrastructure.Security.Tokens.Access.Generator
{
    public class JwtTokenGenerator : JwtTokenHandler, IAccessTokenGenerator
    {
        private readonly uint _tokenLifeTime;
        private readonly string _signinKey;

        public JwtTokenGenerator(uint tokenLifeTime, string signinKey)
        {
            _tokenLifeTime = tokenLifeTime;
            _signinKey = signinKey;
        }

        public string Generate(Guid userIdentifier)
        {
            var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Sid, userIdentifier.ToString())
            };

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.UtcNow.AddMinutes(_tokenLifeTime),
                SigningCredentials = new SigningCredentials(SecurityKey(_signinKey), SecurityAlgorithms.HmacSha256Signature)
            };

            var tokenHandler = new JwtSecurityTokenHandler();

            var securityToken = tokenHandler.CreateToken(tokenDescriptor);

            return tokenHandler.WriteToken(securityToken);
        }
    }
}

Esta classe implementa a interface IAccessTokenGenerator e é usada para gerar tokens de acesso JWT. Ela tem dois campos privados, _tokenLifeTime e _signinKey, que são usados para definir o tempo de vida do token e a chave de assinatura, respectivamente. O método Generate cria um novo token JWT com o identificador do usuário fornecido como uma reivindicação e retorna a string do token.

JwtTokenValidator

using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;

namespace MyGeneralNotes.Infrastructure.Security.Tokens.Access.Validator
{
    public class JwtTokenValidator : JwtTokenHandler, IAccessTokenValidator
    {
        private readonly string _signinKey;

        public JwtToken Validator(string signinKey)
        {
            _signinKey = signinKey;
        }

        public Guid ValidateAndGetUserIdentifier(string token)
        {
            var validationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                IssuerSigningKey = SecurityKey(_signinKey),
                ClockSkew = new TimeSpan(0)
            };

            var tokenHandler = new JwtSecurityTokenHandler();

            var principal = tokenHandler.ValidateToken(token, validationParameters, out _);

            var userIdentifier = principal.Claims.First(c => c.Type == ClaimTypes.Sid).Value;

            return Guid.Parse(userIdentifier);
        }
    }
}

Esta classe implementa a interface IAccessTokenValidator e é usada para validar tokens de acesso JWT. Ela tem um campo privado _signinKey que é usado como a chave de assinatura durante a validação. O método ValidateAndGetUserIdentifier valida o token fornecido e retorna o identificador do usuário do token.

LoggedUser

using Microsoft.EntityFrameworkCore;
using MyGeneralNotes.Domain.Entities;
using MyGeneralNotes.Domain.Security.Tokens;
using MyGeneralNotes.Domain.Services.LoggedUser;
using MyGeneralNotes.Infrastructure.DataAccess;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace MyGeneralNotes.Infrastructure.Services.LoggedUser
{
    public class LoggedUser : ILoggedUser
    {
        private readonly MyGeneralNotesDbContext _context;
        private readonly ITokenProvider _tokenProvider;

        public LoggedUser(MyGeneralNotesDbContext context, ITokenProvider tokenProvider)
        {
            _context = context;
            _tokenProvider = tokenProvider;
        }

        public async Task<User> User()
        {
            var token = _tokenProvider.Value();
            var tokenHandler = new JwtSecurityTokenHandler();

            var jwtSecurityToken = tokenHandler.ReadJwtToken(token);
            var identifier = jwtSecurityToken.Claims.First(c => c.Type == ClaimTypes.Sid).Value;

            var userIdentifier = Guid.Parse(identifier);

            return await _context
                .Users
                .AsNoTracking()
                .FirstAsync(user => user.Active && user.UserIdentifier == userIdentifier);
        }
    }
}

Esta classe implementa a interface ILoggedUser e é usada para operações relacionadas ao usuário logado. Ela tem duas dependências, MyGeneralNotesDbContext e ITokenProvider, que são injetadas através do construtor e armazenadas em campos privados _context e _tokenProvider, respectivamente. O método User recupera o usuário logado do contexto do banco de dados.

UnitOfWork

using MyGeneralNotes.Domain.Repositories;
using System.Threading.Tasks;

namespace MyGeneralNotes.Infrastructure.DataAccess
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly MyGeneralNotesDbContext _context;

        public UnitOfWork(MyGeneralNotesDbContext context)
        {
            _context = context;
        }

        public async Task Commit() => await _context.SaveChangesAsync();
    }
}

Esta classe implementa a interface IUnitOfWork e é responsável por gerenciar as operações de commit no contexto do banco de dados. Ela recebe uma instância de MyGeneralNotesDbContext através do construtor e armazena em um campo privado _context. O método Commit é assíncrono e chama SaveChangesAsync no contexto do banco de dados para persistir todas as alterações realizadas nas entidades durante a transação. Essa classe facilita a realização de transações e garante a consistência dos dados no banco de dados.

DependencyInjectionExtension

using FluentMigrator.Runner;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MyGeneralNotes.Domain.Repositories;
using MyGeneralNotes.Domain.Repositories.Routine;
using MyGeneralNotes.Domain.Repositories.User;
using MyGeneralNotes.Domain.Security.Cryptography;
using MyGeneralNotes.Domain.Security.Tokens;
using MyGeneralNotes.Domain.Services.LoggedUser;
using MyGeneralNotes.Infrastructure.DataAccess;
using MyGeneralNotes.Infrastructure.DataAccess.Repositories;
using MyGeneralNotes.Infrastructure.Extensions;
using MyGeneralNotes.Infrastructure.Security.Cryptography;
using MyGeneralNotes.Infrastructure.Security.Tokens.Access.Generator;
using MyGeneralNotes.Infrastructure.Security.Tokens.Access.Validator;
using MyGeneralNotes.Infrastructure.Services.LoggedUser;
using System;
using System.Reflection;

namespace MyGeneralNotes.Infrastructure
{
    public static class DependencyInjectionExtension
    {
        public static void AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
        {
            AddPasswordEncrypter(services, configuration);
            AddDbContext(services, configuration);
            AddRepositories(services);
            AddMigrations(services, configuration);
            AddTokens(services, configuration);
            AddLoggedUser(services);
        }

        private static void AddDbContext(IServiceCollection services, IConfiguration configuration)
        {
            var connectionString = configuration.ConnectionString();
            var serverVersion = new MySqlServerVersion(new Version(8, 0, 32));

            services.AddDbContext<MyGeneralNotesDbContext>(dbContextOptions =>
            {
                dbContextOptions.UseMySql(connectionString, serverVersion);
            });
        }

        private static void AddRepositories(IServiceCollection services)
        {
            services.AddScoped<IUnitOfWork, UnitOfWork>();

            services.AddScoped<IUserWriteOnlyRepository, UserRepository>()
                    .AddScoped<IUserReadOnlyRepository, UserRepository>()
                    .AddScoped<IUserUpdateOnlyRepository, UserRepository>();

            services.AddScoped<IRoutineWriteOnlyRepository, RoutineRepository>()
                    .AddScoped<IRoutineReadOnlyRepository, RoutineRepository>()
                    .AddScoped<IRoutineUpdateOnlyRepository, RoutineRepository>();
        }

        private static void AddMigrations(IServiceCollection services, IConfiguration configuration)
        {
            var connectionString = configuration.ConnectionString();
            services.AddFluentMigratorCore().ConfigureRunner(opt =>
            {
                opt
                    .AddMySql5()
                    .WithGlobalConnectionString(connectionString)
                    .ScanIn(Assembly.Load("MyGeneralNotes.Infrastructure")).For.All();
            });
        }

        private static void AddTokens(IServiceCollection services, IConfiguration configuration)
        {
            var tokenLifeTime = uint.Parse(configuration.GetSection("Settings:Jwt:TokenLifeTime").Value!);
            var signinKey = configuration.GetSection("Settings:Jwt:SigninKey").Value!;

            services.AddScoped<IAccessTokenGenerator>(opt => new JwtTokenGenerator(tokenLifeTime, signinKey!));
            services.AddScoped<IAccessTokenValidator>(opt => new JwtTokenValidator(signinKey!));
        }

        private static void AddLoggedUser(IServiceCollection services) => services.AddScoped<ILoggedUser, LoggedUser>();

        private static void AddPasswordEncrypter(IServiceCollection services, IConfiguration configuration)
        {
            var additionalKey = configuration.GetSection("Settings:Password:AdditionalKey").Value;
            services.AddScoped<IPasswordEncrypter>(opt => new Sha512Encrypter(additionalKey!));
        }
    }
}

Esta classe estática é responsável por centralizar a configuração e registro de todos os serviços necessários para a camada de infraestrutura do aplicativo. Seu principal objetivo é facilitar a injeção de dependência e promover uma melhor organização do código. Aqui estão os detalhes:

  1. AddInfrastructure: Este é o método principal que adiciona todos os serviços necessários ao contêiner de injeção de dependência. Ele chama outros métodos privados para adicionar serviços específicos.

  2. AddPasswordEncrypter: Este método adiciona o serviço IPasswordEncrypter ao contêiner de injeção de dependência. Ele usa a chave adicional da configuração para criar uma nova instância de Sha512Encrypter.

  3. AddDbContext: Este método adiciona o contexto do banco de dados MyGeneralNotesDbContext ao contêiner de injeção de dependência. Ele usa a string de conexão da configuração para configurar o contexto do banco de dados.

  4. AddRepositories: Este método adiciona os repositórios IUnitOfWork, IUserWriteOnlyRepository, IUserReadOnlyRepository, IUserUpdateOnlyRepository, IRoutineWriteOnlyRepository, IRoutineReadOnlyRepository e IRoutineUpdateOnlyRepository ao contêiner de injeção de dependência.

  5. AddMigrations: Este método adiciona o serviço FluentMigratorCore ao contêiner de injeção de dependência. Ele usa a string de conexão da configuração para configurar o serviço de migração.

  6. AddLoggedUser: Este método adiciona o serviço ILoggedUser ao contêiner de injeção de dependência. Ele instancia uma nova instância de LoggedUser.

  7. AddTokens: Este método adiciona os serviços IAccessTokenGenerator e IAccessTokenValidator ao contêiner de injeção de dependência. Ele usa o tempo de vida do token e a chave de assinatura da configuração para criar novas instâncias de JwtTokenGenerator e JwtTokenValidator.

Entendido! Aqui está o encerramento do diretório de infraestrutura, seguindo o modelo que você forneceu:


Encerramento do Diretório de Infraestrutura

Neste tópico, foi explorado em detalhes o diretório de Infraestrutura da nossa aplicação, localizado dentro da pasta 'back'. Este diretório descreve a implementação técnica dos componentes que dão suporte à execução da lógica de negócios da nossa aplicação, incluindo acesso a bancos de dados, serviços externos e outros recursos.

Ao longo desta documentação, foi examinado os seguintes aspectos fundamentais da infraestrutura:

Camada de Acesso a Dados

Na camada de Infraestrutura, encontramos os componentes responsáveis pelo acesso a dados da aplicação. Isso inclui a classe MyGeneralNotesDbContext, que representa o contexto do Entity Framework Core e fornece acesso aos conjuntos de entidades, gerenciamento de transações e mapeamento objeto-relacional.

Além disso, foi demonstrado os repositórios de dados, como UserRepository e RoutineRepository, que implementam as interfaces definidas no domínio e são responsáveis por interagir com o banco de dados para realizar operações relacionadas às entidades do sistema.

Segurança

No que diz respeito à segurança, verificamos classes como Sha512Encrypter para criptografar senhas de forma segura e JwtTokenGenerator e JwtTokenValidator para gerar e validar tokens de acesso JWT, garantindo a autenticação e autorização adequadas em nossa aplicação.

Gerenciamento de Migrações

Para manter a consistência do esquema do banco de dados ao longo do tempo, foi utilizado o FluentMigrator para gerenciar migrações de banco de dados de forma eficiente. A classe DatabaseMigration é responsável por criar o banco de dados (se ainda não existir) e executar migrações pendentes.

Outras Implementações

Além disso, foi explorado outras implementações importantes, como LoggedUser, que fornece funcionalidades relacionadas ao usuário logado, e UnitOfWork, que gerencia as operações de commit no contexto do banco de dados.

Com uma compreensão sólida do diretório de Infraestrutura, será apresentada a próxima etapa, onde será exposta a camada de Exceções da aplicação. Esta camada descreve como lidamos com erros e exceções em toda a aplicação, garantindo uma experiência confiável para os usuários finais.


Próxima: MyGeneralNotes.Exceptions

⚠️ **GitHub.com Fallback** ⚠️