C# - Criando API com Jwt Token - Autorização e Autenticação - Módulo 3 - Arquitetura 3 camadas - Repository Pattern
Published: 2022-04-07 20:30:10

Sejam Bem Vindos a mais um módulo da nossa série.
&nbsp;
Código Modulo 3 - [Repositório GitHub](https://github.com/mgpaixao/JWTAuthAuthenticationProject/tree/master/Modulo%203)
&nbsp;


Primeiramente. Olá

![Gifss](https://media.giphy.com/media/3oz8xSjBmD1ZyELqW4/giphy.gif)

# Melhorias desse modulo


- CRUD de Usuários
- Introdução do Repository Pattern
- Divisão em 3 Projetos (Infra - Domain - API)


Nesse módulo, irei abordar a evolução do módulo anterior, será muito fácil se perder na nova arquitetura que implementei, pois são vários caminhos percorridos para conseguir executar um end point. 

# Divisão em 3 Camadas

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pjws8zrgrcap5x86dgh3.png)
 
- API 
 - Na camada de API colocamos os itens principais do projeto, Controllers, as models de entrada e saída (RequestDTO e ResponseDTO) e as configurações pertinentes a `program.cs(.net 6)` ou `startup.cs (.net 5 ou anterior)`.
&nbsp;
- INFRA
 - Na camada de infraestrutura colocamos toda a infra de banco de dados e repositórios. (Context, Mapeamentos, Repositories)
- DOMAIN
 - Na camada de Domain colocamos tudo que é compartilhado entre os outros projetos, as Models, os Services, Utilitários, Interfaces e helpers compartilhados entre os outros projetos. Domain é onde fica toda a regra de negócio da aplicação onde será compartilhado com as outras camadas.

# Arquitetura 3 camadas
&nbsp;
A arquitetura em 3 camadas serve para dividir as responsabilidades entre 3 projetos, podendo cada um focar em sua parte. Por exemplo, separando a camada de domínio e colocando toda a regra de negócio apenas ali, facilita assim, a manutenção do código, pois você já sabe que cada tecnologia, cada parte de código, está em seu devido lugar. 

# Como dividir o projeto em 3 camadas?
#### Criando Biblioteca de classes

Entenda que dividir o projeto em camadas nada mais é do que você criar sua própria biblioteca para consumo interno. 

![Criando Projeto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oaww8wq7806fkazzid7r.gif)

Para fazer a comunicação entre as camadas, é necessário referencia-las como no gif abaixo.

![referencias](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ko90v60ntkri2dkdb7l.gif)
 
Repare que o projeto `domain`, não contem referências, pois na verdade ele não depende de ninguém, são os outros projetos que dependem dele, pois é ali que se concentra toda a regra de negócio que precisa ser consumida pela infraestrutura e pelas controllers no projeto principal. 

# Repository Pattern 

![Repo Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2bex6i1ys4hltrasuz2m.png)
 
O `repository pattern` é um dos padrões de projetos mais utilizamos mundialmente, ele é amplamente conhecido e aceito em projetos de todos os níveis. 

Esse padrão de projeto visa a abstração de responsabilidade e encapsulamento do banco de dados, possibilitando a injeção de dependência. 

Mas o que isso significa? Basicamente, você pode ter o banco de dados que for, e isso não irá afetar o resto do sistema. Pois agora cada um tem sua responsabilidade, se o repositório é responsável pelo acesso ao banco de dados, então quando tivermos alguma alteração de banco de dados, já sabemos que a alteração é mais provável que seja apenas ali.

# Implementação

Primeiro de tudo eu crio o Repositório base, tem muitos exemplos prontos disponíveis pela internet com os métodos já prontos.

Entenda que pra todo repositório, ou serviço criado, você precisa também criar a Interface, que é o método de acesso a essa classe.

# Base Repository

É aqui o único lugar onde terá uma estância do `DataContext` que é nosso acesso ao Banco de dados.

Os repositórios sempre implementam os métodos definidos na interface dele, e representam de forma genérica uma entidade/model.

```
public class BaseRepository<T> : IBaseRepository<T> where T : Entity
    {
        private readonly DbSet<T> DbSet;
        private readonly DataContext _context;

        protected BaseRepository(DataContext context)
        {
            _context = context;
            DbSet = _context.Set<T>();
        }

        public async Task<T?> GetById(Guid id) =>
        await DbSet.FindAsync(id);

        public async Task<IEnumerable<T>> GetAllAsync() =>
            await DbSet.ToListAsync();

        public async Task<T?> GetOneBy(Expression<Func<T, bool>> expression) =>
            await DbSet.AsNoTracking().FirstOrDefaultAsync(expression);

        public async Task<IEnumerable<T>> GetListBy(Expression<Func<T, bool>> expression) =>
            await DbSet.Where(expression).ToListAsync();

        public async Task<bool> Exists(Expression<Func<T, bool>> expression) =>
            await DbSet.AnyAsync(expression);

        public async Task AddAsync(T entity)
        {
            await DbSet.AddAsync(entity);
            await SaveChanges();
        }

        public void Update(T entity) =>
            DbSet.Update(entity);

        public void Remove(T entity) =>
            DbSet.Remove(entity);

        public async Task<int> SaveChanges()
        {
            return await _context.SaveChangesAsync();
        }

        public void Dispose()
        {
            _context?.Dispose();
        }
    }
```


#IBaseRepository Interface

Notem que a interface tem todos os métodos que implementamos no repositório base.

A interface é o método de acesso aos repositórios e serviços. 

```
public interface IBaseRepository<T> : IDisposable where T : Entity
    {
        Task<T> GetById(Guid id);
        Task<IEnumerable<T>> GetAllAsync();
        Task<T> GetOneBy(Expression<Func<T, bool>> expression);
        Task<IEnumerable<T>> GetListBy(Expression<Func<T, bool>> expression);
        Task<bool> Exists(Expression<Func<T, bool>> expression);
        Task<int> SaveChanges();
        Task AddAsync(T entity);
        public void Update(T entity);
        public void Remove(T entity);
    }
```

# Service Login

Vou exemplificar como funciona o fluxo com o serviço de Login.

Repare que estanciamos o repositório de Login, que lá dentro acessa o repositório base que tem acesso ao banco de dados. Antes de chegar aqui, esse serviço é estanciado na controller através de sua interface.

```
public class LoginService :  ILoginService
    {
        private readonly ILoginRepository _loginRepository;
        private readonly TokenService _tokenService;

        public LoginService(ILoginRepository loginRepository, TokenService tokenService)
        {
            _tokenService = tokenService;
            _loginRepository = loginRepository;
        }

        public async Task<string> LoginAsync(string email, string password)
        {
            var user = await _loginRepository.GetUser(email);

            if (user == null)
                return "User or password invalid";

            if (!PasswordHasher.Verify(user.PasswordHash, password))
                return "User or password invalid";

            try
            {
                var token = _tokenService.GenerateToken(user);
                return token;
            }
            catch
            {
                return "Internal Error";
            }
        }

        public void Dispose()
        {
            _loginRepository?.Dispose();
        }
    }
```

# Controller

Terminando aqui o fluxo reverso de como acessar o banco de dados com o repository pattern, podemos ver que na controller estanciamos a interface do serviço de interesse.

```
public class AuthController : ControllerBase
    {
        private readonly ILoginService _loginService;
        public AuthController(ILoginService loginService)
        {
            _loginService = loginService;
        }
```

Colocarei aqui novamente a imagem do fluxo de dados no repository pattern. Da controller você acessa os serviço de interesse, onde tem toda a regra de negócio da entidade em questão, depois acessa o repositório da entidade através da interface, e por fim, acessa o repositório base pra acesso aos dados.

![Images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9eas9dfisu9gxd0usgd4.png)

Pra tudo isso funcionar, temos que declarar as injeções de dependências, pois é dessa forma que estamos estanciando uma classe na outra.

Sempre declarar na ordem da esquerda pra direita no fluxo de dados, então primeiro a interface depois sua classe de implementação.

```
public static IServiceCollection ResolveDependencies(this IServiceCollection services)
        {
            services.AddDbContext<DataContext>();
            services.AddScoped<TokenService>();

            services.AddScoped<ILoginService, LoginService>();
            services.AddScoped<ILoginRepository, LoginRepository>();

            services.AddScoped<IRoleRepository, RoleRepository>();
            services.AddScoped<IRoleService, RoleService>();

            services.AddScoped<IUserService, UserService>();
            services.AddScoped<IUserRepository, UserRepository>();

            return services;
        }
```
# Porque implementar tudo isso?

![garoa](https://media.giphy.com/media/0Vv0Ne2CnOClIExIuL/giphy.gif)


Temos diversos padrões e designs de arquiteturas a adotar, no fim das contas implemente aquele que mais se encaixa com sua demanda. 
Se você vai fazer uma API simples, com 2 models, 1 controller e um crud simples, talvez não faça sentido você aplicar essa complexidade. Mas é uma decisão e se tomar com o seu time de desenvolvimento.

Lembrando os benefícios que você aplicar conceitos como S.O.L.I.D e Repository pattern por exemplo.

- O sistema ser agnóstico de banco de dados, você pode trocar o banco de dados com facilidade sem afetar o resto do código.
- Código centralizado em um único ponto, evitando duplicidade.
- Facilita a implementação de testes. Em caso de testes mais avançados, talvez você não consiga fazer sem adotar uma arquitetura mais organizada.
- Diminui o acoplamento entre classes.
- Padronização de códigos e serviços.
- Facilita a reutilização de código.


Fique ligado no próximo artigo da série, onde vamos adicionar o padrão de UOW - Unity of Work para acesso ao banco de dados.


Confira os módulos anteriores


[Modulo 1](https://dev.to/mgpaixao/criando-api-com-jwt-autorizacao-e-autenticacao-modulo-1-20b8)
[Modulo 2](https://dev.to/mgpaixao/c-criando-api-login-com-jwt-token-autorizacao-e-autenticacao-modulo-2-5cf)

![tHANKS](https://media.giphy.com/media/AeWoyE3ZT90YM/giphy.gif)
 
{% user mgpaixao %}