- Download .NET Core 2.2.0 (SDK 2.2.101) -> https://dotnet.microsoft.com/download/dotnet-core/2.2
- .NET Framework 4.7.2 -> https://dotnet.microsoft.com/download/thank-you/net472
- ASP.NET Core 2.2 (with .NET Core 2.2)
- ASP.NET MVC Core 2.2
- ASP.NET WebApi Core 2.2
- ASP.NET Identity Core 2.2
- Entity Framework Core 2.2
- .NET Core Native DI
- AutoMapper
- FluentValidator
- MediatR
- Swagger UI
- Full architecture with responsibility separation concerns, SOLID and Clean Code
- Domain Model Pattern, CQRS and ES concepts
- Domain Driven Design (Layers and Domain Model Pattern)
- Domain Events
- Domain Notification
- CQRS - Command Query Responsibility Segregation
- Event Sourcing
- Unit of Work
- Repository and Generic Repository
- ASP.NET Identity working throught WebAPI services
- Different ways to read and write data
- Unit Tests
- Integration Tests
- Microsft Windows 10 Pro
- Microsoft Visual Studio Professional 2019 (versão 16.0.2)
- Docker Desktop Community Version 2.0.0.3 (31259) - Channel: stable - Build: 8858db3
- Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64) Developer Edition
- SQL Server Management Studio v17.4
O desafio proposto pela Stone Payments pode ser acessado aqui -> https://github.com/stone-payments/microtef-hire-me
Basicamente o desafio consiste na implementação de um sistema que simule o processo de uma transação financeira.
Para simular um sistema de transação financeira foi implementado:
- Cliente WPF
- Servidor de comunicações
-
Cliente WPF (AmonRa)
-
Deve haver 4 telas principais:
- Tela de transação: input dos dados da transação e envio da transação para o servidor
- Tela de consulta das transações efetuadas: lista das transações efetuadas
- Sondagem das transações: listagem de todas as operações realizadas referente a um único cartão
- Cadastro de clientes: deverá ser capaz de cadastrar clientes que possam passar transações
-
Catálogo de cartões virtuais com o número razoável de cartões para testar diferentes cenários
- Propriedades básicas
- A senha de cada cartão do catálogo deve estar criptografada de algum jeito
- Com esse catálogo, a verificação da senha do cartão deve ser feita apenas pelo servidor
- Propriedades básicas
-
-
Servidor Comunicações (Karnak)
- O servidor irá simular justamente o que a Stone é, uma adquirente
- Tipos de cartão aceitos: crédito e débito
- Bandeiras: Visa, MasterCard, American Express
- O servidor deve esperar por uma transação
-
Limite de Crédito
- O limite de crédito para cada cliente deve ser considerado
-
Comunicação entre cliente e servidor
- A comunicação pode acontecer em JSON ou XML
Como em qualquer transação do Mundo real, na solução do desafio proposta não foi diferente.
Senha sempre é uma questão delicada, para não termos nenhum problema as senhas sempre são transmitidas de forma criptografada.
As senhas enviadas do cliente WPF (AmonRa) para o servidor de comunicações (Karnak) são transmitidas de forma criptografada utilizando criptografia de 256 bits.
A verificação da senha fica a cargo do servidor de comunicações (Karnak), o qual verifica se a senha informada é a mesma armazenada no banco de dados.
Todas as senhas armazenadas no banco de dados são criptografadas utilizando criptografia de 256 bits.
Veja abaixo as senhas criptografadas no banco de dados.
Os cartões armazenados no banco de dados possuem senha 985471
Para gerar uma imagem docker do servidor de comunicações seguir os passos:
- Abrir uma tela do PowerShell ou Command Prompt em modo Administrador
- Acessar a pasta do codigo fonte do projeto /KarnakCore/src/
- cd \microtef-hire-me6\Desafio_Stone\KarnakCore\src
- Digitar o command docker build -t karnakservicesapi:dev .
- Tagear a imagem: docker tag 0792b7f18a26(id da imagem) stefansilva/stefansilva:karnakstone
- Enviar para Docker Hub a imagem criada: docker push stefansilva/stefansilva:karnakstone
- docker pull stefansilva/stefansilva:karnakstone
Para iniciar a imagem Docker do servidor de comunicações seguir os passos:
- Abrir uma tela do PowerShell ou Command Prompt em modo Administrador
- Digitar o comando docker images
- Localizar o nome da imagem karnakstone
- Pegar o IMAGE ID
- Digitar o comando: docker run -p 5001:80 95255d998610
- Onde: -p: porta
- 5001: porta de acesso (binding)
- 80: porta interna
- 95255d998610: id da imagem
Se tudo deu certo, o resultado esperado é: PS C:\Users\stefa> docker run -p 5001:80 95255d998610 info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using '/root/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[58] Creating key {12f361a1-c392-4f68-b8ba-9d962a43da1f} with creation date 2019-04-30 09:53:23Z, activation date 2019-04-30 09:53:23Z, and expiration date 2019-07-29 09:53:23Z. warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35] No XML encryptor configured. Key {12f361a1-c392-4f68-b8ba-9d962a43da1f} may be persisted to storage in unencrypted form. info: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[39] Writing data to file '/root/.aspnet/DataProtection-Keys/key-12f361a1-c392-4f68-b8ba-9d962a43da1f.xml'. Hosting environment: Production Content root path: /app Now listening on: http://[::]:80 Application started. Press Ctrl+C to shut down.
PARA ACESSAR API´S REST DO SERVIDOR, UTILIZAR POR EXEMPLO A URL http://192.168.0.11:5001/api/v1/cardtype-management/
O servidor de comunicações possuí diversas api´s rest de acesso. Para saber quais são acessar os controllers da aplicação.
Dica 1: para ver os logs em tempo real dos containers docker utilizar o comando docker logs -f , exemplo: docker logs -f karnakservicesapi
Dica 2: Dockerhub, pull e push nas suas imagens -> https://medium.com/trainingcenter/docker-dockerhub-pull-e-push-nas-suas-imagens-57dffa0232ad
Como alternativa a linha de comando podemos utilizar o Visual Studio Code. Para alterar o IP de conexão com o banco de dados, seguir os passos abaixo:
O banco de dados Microsoft SQL Server foi instalado dentro de um container Docker
===================[ IMPORTANTE!!! ]===================
- Para restaurar o banco de dados, o arquivo KarnakStoneV13.bak deve ser copiado para a pasta *C:\SQLServer2017*
A Microsoft disponibilizou informações de como Configurar imagens de contêiner do SQL Server no Docker o link esta aqui -> https://docs.microsoft.com/pt-br/sql/linux/sql-server-linux-configure-docker?view=sql-server-2017
Os procedimentos de instalação do Microsoft SQL Server dentro de um container Docker podem ser visualizadas aqui -> https://medium.com/@renato.groffe/net-sql-server-2017-parte-1-executando-o-sql-server-em-um-container-docker-83abbed8eb7e
Os dados utilizados para instalação:
- Nome do servidor: localhost ou o IP onde está instalado o banco de dados, 11433
- Exemplo: localhost, 11433
- Autenticação:
- Usuario: sa
- Senha: DockerSql2017!
- O scrit sql para geração dos objetos está aqui -> https://github.com/silvastefan/microtef-hire-me/blob/master/Desafio_Stone/KarnakCore/sql/GenerateDataBase.sql
- O backup do banco de dados pode ser encontado aqui -> https://github.com/silvastefan/microtef-hire-me/blob/master/Desafio_Stone/KarnakCore/sql/KarnakStoneV13.bak
Os dados são armazenados no banco de dados Microsoft SQL Server.
A estrutura do banco de dados:
Para realizar a criptografia e descriptografia da senha foi utilizado a classe StringCipher.
Para atender ao desafio proposto pela Stone foi estruturado um banco de dados com algumas tabelas básicas para a operação do sistema.
O sistema é composto por sete tabelas, abaixo suas estruturas e seus relacionamentos.
Banco de dados - Mapeamento ORM
- Senha inválida
- Cartão bloqueado
- Mínimo de 10 centavos
- Registro não encontrado
- Senha Incorreta
- Aprovado
- Valor inválido
- Transação aprovada
- Saldo insuficiente
- Senha entre 4 e 6 dítigos
- Transação negada
- Erro no tamanho da senha
- Cartão vencido
Por se tratar de um desafio, no qual são realizadas transações com cartões, visando uma maior segurança e rastreabilidade optou-se por implmentar o Event Sourcing.
A finalidade do Event Sourcing é armazenar no banco de dados histórico de todas as operações recebidas ou enviadas pelo servidor de comunicações (Karnak).
- Tipo Transação
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Status Transação
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Bandeira Cartão
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Tipo Cartão
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Clientes
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Cartões
- Incluir
- Alterar
- Excluir
- Consultar
- Por nome
- Por id
- Listagem
- Historico
- Transações
- Incluir
- Consultar
- Por id
- Sondagem das transações efetuadas
- Listagem somente das transações efetuadas
- Listagem das transações com os relacionamentos de dados
- AmonRa - cliente WPF
- EFCoreMapStone - entity framework, cria o banco de dados, as tabelas, PK´s e FK´s
- UnitTesteKarnakStone - responsável por realizar os testes unitários e de integração
- KarnakCore - o coração do projeto, responsável por tudo, é o cara!
O nome AmonRa foi escolhido por se tratar do pai dos Deuses, o senhor da verdade, no antigo egito.
Para atender aos requisitos do desafio, foram criadas telas adicionais:
Cliente WPF - Tela Tipo Transações
Cliente WPF - Tela Tipo Transações
Cliente WPF - Tela Bandeira Cartao
Cliente WPF - Tela Tipo Cartão
Cliente WPF - Tela Listagem Transacoes
Cliente WPF - Tela Sondagem das Transacoes
Cliente WPF - Tela Cadastro Cliente
O projeto está estruturado da seguinte forma:
- Pasta Common: classes comuns ao projeto
- Classe StringCipher.cs: responsável por realizar a criptografia e descriptografia da senha
- Pasta Core: responsável por efetuar transações
- Classe TransactionServer.cs: enviar transações para o servidor Karnak
- Pasta Models: os modelos de dados
- São as classes que ajudam na realização dos parser´s dos dados que são enviados para o servidor ou que chegam do mesmo
- Pasta SendRequestToServer: enviar requisições para o servidor Karnak
- São as classes que realizam conexão diretamente com o api rest do servidor Karnak
- Pasta Services: são os serviços que chamam as classes do passo 4
- As telas WPF chamam as classes de serviços, que por sua vez chamam as classes do passo 4
São os cartões cadastrados no banco de dados, as informações dos cartões são:
- Número do cartão (cardnumber)
- Senha do cartão (password), a senha será exigida somente para cartões com chip
- Valor da transação (amount)
- Tipo da transação (crédito ou débito)
- Pacelas, quantidade de parcelas exibida somente para compras do tipo crédito
- Validade do cartão
- Bandeira do cartão (visa, master, amex)
Para ver as transações realizads por algum cartão, é necessário escolher um cartão na tela da Listagem Transações.
São todas as transações realizadas pelo servidor. É possível acompanhar o histórico das transações de um determinado cartão. Todas as transações possuem status de aprovada ou negada.
Passos para realizar uma transação:
- Informar o valor
- Escolher um cartão do catálogo
- Se o cartão escolhido for do tipo CHIP o campo de senha SERÁ exibido
- Se o cartão escolhido for do tipo TARJA o campo de senha NÃO SERÁ exibido
- Escolher crédito ou débito
- Se a opção escolhida for CRÉDITO a opção parcelamento SERÁ exibida
- Se a opção escolhida for DÉBITO a opção parcelamento NÃO SERÁ exibida
- Confirmar a transação pressionando o botão
- Se ao pressionar o botão CONFIRMAR estiver faltando alguma informação obrigatória, um alerta será exibido
- Pressionou o botão e todas as informações preenchidas
- Será enviada para o servidor de comunicações (Karnak) uma requisição para realizar uma transação
- O servidor irá validar as informações enviadas, caso ocorra alguma restrição, um ou mais erros podem ser retornados:
- Diversas validações são realizadas pelo servidor de comunicações, caso alguma não seja satisfatória o mesmo irá retornar com erro. Os possíveis retornos podem ser vistos no item Possíveis Retornos do Servidor
Passos para realizar o cadastro de um cliente e cartão
- Informar o nome
- Informar o e-mail
- Informar data de aniversário
- Escolher tipo do cartão
- Se cartão escolhido for CHIP a opção para informar senha do cartão será exibida
- Escolher a bandeira do cartão
- Informar o número do cartão
- Data de expiração do cartão
- Limite do cartão
- Pressionar Salvar
- Em caso de falha
- Todas as informações serão validatas, caso alguma não satisfaça alguma condição, um ou mais erros podem retornar
- Validações diferentes são realizadas para cartão e cliente
- Caso o servidor de comunicações retorne erro(s) os mesmos serão exibidos na tela
- Diversas validações são realizadas pelo servidor de comunicações, caso alguma não seja satisfatória o mesmo irá retornar com erro. Os possíveis retornos podem ser vistos no item Possíveis Retornos do Servidor
- Em caso de sucesso
- Uma mensagem de SUCESSO será exibida para o cadastro do cliente e do cartão
- Em caso de falha
O arquivo App.config possuí a KEY de configuração de conexão com o servidor de comunicações (Karnak).
Caso seja necessário realizar a troca da url, o arquivo está aqui -> https://github.com/silvastefan/microtef-hire-me/blob/master/Desafio_Stone/AmonRa/AmonRa/App.config
Após criar as tabelas necessárias e realizar os devidos relacionamentos, configurar o arquivo KarnakContext.cs
Pesquisar no arquivo pelo termo "optionsBuilder.UseSqlServer" e realizar os ajustes necessários.
Seguir os passos:
- dotnet ef migrations add mig1 -p EFCoreMapStoneV13
- verificar se na lista de migracoes disponiveis esta a nova migracao 'mig1'
- dotnet ef migrations list -p EFCoreMapStoneV13
- Wooouuuu, tudo pronto! Agora vamos criar o banco de dados e as tabelas.
- dotnet ef database -v update -v -p EFCoreMapStoneV13
- se tudo correu bem, os scripts devem começar a serem exibidos
Testes unitários foram realizados em todas as api´s rest.
Abaixo a lista de todos os testes efetuados.
- Bandeira Cartão
- Post_CardBrand_The_Name_Is_Required
- Delete_ByIdCardBrand_Valido
- Get_ByNameCardBrand
- Delete_ByIdCardBrand_Registro_nao_encontrado
- Post_CardBrand_Valido
- Put_CardBrand_The_Name_must_have_between_2_and_30_characters
- Put_CardBrand_The_Guid_is_invalid_and_contains_00000000
- Post_CardBrand_The_card_brand_id_has_already_been_taken_Run_2_Times
- Post_CardBrand_The_Guid_is_empty
- Put_CardBrand_Valido
- Post_CardBrand_The_Guid_is_invalid_and_contains_00000000
- Put_CardBrand_The_Name_is_Required
- Post_CardBrand_The_card_brand_name_has_already_been_taken_Run
- Delete_ByIdCardBrand_The_Guid_is_empty
- Get_AllCardBrand
- Put_CardBrand_The_card_brand_name_has_already_been_taken
- Get_HistorycByIdCardBrand
- Get_ByIdCardBrand
- Put_CardBrand_The_Guid_is_empty
- Post_CardBrand_The_Name_must_have_between_2_and_30_characters
- Delete_ByIdCardBrand_The_Guid_is_invalid_and_contains_00000000
- Tipo Cartão
- Put_CardType_The_card_type_name_has_already_been_taken
- Get_AllCardType
- Post_CardType_Valido
- Delete_ByIdCardType_The_Guid_is_invalid_and_contains_00000000
- Delete_ByIdCardType_Registro_nao_encontrado
- Post_CardType_The_card_type_id_has_already_been_taken_Run_2_Times
- Get_HistorycByIdCardType
- Post_CardType_The_Guid_is_empty
- Post_CardType_The_Name_must_have_between_2_and_30_characters
- Get_ByIdCardType
- Put_CardType_The_Name_must_have_between_2_and_30_characters
- Post_CardType_The_Name_Is_Required
- Put_CardType_The_Name_is_Required
- Put_CardType_Valido
- Post_CardType_The_card_type_name_has_already_been_taken_Run
- Get_ByNameCardType
- Post_CardType_The_Guid_is_invalid_and_contains_00000000
- Put_CardType_The_Guid_is_invalid_and_contains_00000000
- Delete_ByIdCardType_The_Guid_is_empty
- Delete_ByIdCardType_Valido
- Put_CardType_The_Guid_is_empty
- Cartão
- Get_ByCardNumber
- Get_HistorycByIdCard
- Get_ByIdCard
- Delete_ByIdCard_Valido
- Post_Card_Valido
- Get_AllCard
- Cliente
- Post_Customer_The_Name_must_have_between_2_and_30_characters
- Post_Customer_Valido
- Put_Customer_The_Name_must_have_between_2_and_100_characters
- Put_Customer_Valido
- Put_Customer_The_Guid_is_invalid_and_contains_00000000
- Post_Customer_The_Guid_is_empty
- Put_Customer_The_customer_name_has_already_been_taken
- Post_Customer_The_customer_e_mail_has_already_been_taken_Run_2_Times
- Put_Customer_The_Name_is_Required
- Delete_ByIdCustomer_The_Guid_is_empty
- Delete_ByIdCustomer_The_Guid_is_invalid_and_contains_00000000
- Put_Customer_The_Guid_is_empty
- Post_Customer_The_customer_id_has_already_been_taken_Run
- Post_Customer_The_Name_Is_Required
- Post_Customer_The_Guid_is_invalid_and_contains_00000000
- Delete_ByIdCustomer_Valido
- Delete_ByIdCustomer_Registro_nao_encontrado
- Get_AllCustomer
- Get_ByIdCustomer
- Get_HistorycByIdCustomer
- Get_ByNameCustomer
- Senha
- PasswordDecryptFail
- PasswordDecryptSuccess
- PasswordEncrypt
- Transação Status
- Get_ByIdTransactionStatus
- Put_TransactionStatus_The_Guid_is_empty
- Put_TransactionStatus_The_Name_must_have_between_2_and_30_characters
- Delete_ByIdTransactionStatus_Valido
- Delete_ByIdTransactionStatus_The_Guid_is_invalid_and_contains_00000000
- Get_HistorycByIdTransactionStatus
- Delete_ByIdTransactionStatus_Registro_nao_encontrado
- Post_TransactionStatus_The_Name_must_have_between_2_and_30_characters
- Post_TransactionStatus_The_Guid_is_invalid_and_contains_00000000
- Get_AllTransactionStatus
- Put_TransactionStatus_The_transaction_status_name_has_already_been_taken
- Get_ByNameTransactionStatus
- Put_TransactionStatus_Valido
- Put_TransactionStatus_The_Name_is_Required
- Post_TransactionStatus_The_transaction_status_id_has_already_been_taken_Run_2_Times
- Post_TransactionStatus_The_Guid_is_empty
- Post_TransactionStatus_Valido
- Delete_ByIdTransactionStatus_The_Guid_is_empty
- Put_TransactionStatus_The_Guid_is_invalid_and_contains_00000000
- Post_TransactionStatus_The_Name_Is_Required
- Post_TransactionStatus_The_transaction_status_name_has_already_been_taken
- Transação Tipo
- Post_TransactionType_The_Name_Is_Required
- Delete_ByIdTransactionType_The_Guid_is_empty
- Get_HistorycByIdTransactionType
- Post_TransactionType_The_Guid_is_invalid_and_contains_00000000
- Post_TransactionType_The_transaction_type_name_has_already_been_taken
- Get_ByIdTransactionType
- Post_TransactionType_The_Name_must_have_between_2_and_30_characters
- Delete_ByIdTransactionType_Valido
- Get_AllTransactionType
- Put_TransactionType_The_Name_is_Required
- Put_TransactionType_The_transaction_type_name_has_already_been_taken
- Delete_ByIdTransactionType_The_Guid_is_invalid_and_contains_00000000
- Get_ByNameTransactionType
- Post_TransactionType_Valido
- Put_TransactionType_The_Guid_is_empty
- Put_TransactionType_The_Name_must_have_between_2_and_30_characters
- Post_TransactionType_The_Guid_is_empty
- Delete_ByIdTransactionType_Registro_nao_encontrado
- Put_TransactionType_Valido
- Put_TransactionType_The_Guid_is_invalid_and_contains_00000000
- Post_TransactionType_The_transaction_type_id_has_already_been_taken_Run_2_Times
- Transação
- Post_Transaction_CHIP
- Delete_ByIdTransaction_Valido
- Post_Transaction_TARJA
- Get_HistorycByIdTransaction
- Get_ByIdTransaction
- Post_Transaction_Cartao_Vencido
- Get_AllTransaction
- Get_ByNameTransaction
- Para que a conexão com banco de dados funcione é necessário configurar o arquivo appsettings.json
- Na key ConnectionStrings alterar DefaultConnection para o IP do computador onde está rodando o servidor de comunicações
- Setar o projeto Karnak.Services.Api como StartUp Project
O servidor de comunicações é o projeto responsável por fazer tudo funcionar, ele é o cara!
Por que o nome Karnak:
Templo de Carnaque, ou simplesmente Carnaque, é um templo dedicado ao deus Amom-Rá.
Tem esse nome devido a uma aldeia vizinha chamada Carnaque, mas no tempo dos antigos faraós a aldeia era conhecida como Ipet-sut.
Maiores informações sobre o templo de Karnak aqui -> https://www.penaestrada.blog.br/luxor-o-templo-de-karnak/
CQRS significa Command Query Responsibility Segregation. Como o nome já diz, é sobre separar a responsabilidade de escrita e leitura de seus dados.
CQRS é um pattern, um padrão arquitetural assim como Event Sourcing, Transaction Script e etc.
O CQRS não é um estilo arquitetural como desenvolvimento em camadas, modelo client-server, REST e etc.
Atualmente as aplicações não são mais para atender dez usuários simultâneos, a maioria das novas aplicações nascem com premisas de escalabilidade, performance e disponibilidade, fazer uma aplicação funcionar bem para cargas de trabalho de forma elástica é uma tarefa extremamente complexa.
O CQRS prega a divisão de responsabilidade de gravação e escrita de forma conceitual e física.
Isto significa que além de ter meios separados para gravar e obter um dado os bancos de dados também são diferentes.
As consultas são feitas de forma síncrona em uma base desnormalizada separada e as gravações de forma assíncrona em um banco normalizado.
A ideia básica é segregar as responsabilidades da aplicação em:
- Command
- Operações que modificam o estado dos dados na aplicação.
- Query
- Operações que recuperam informações dos dados na aplicação.
Para resolver o desafio foi utilizado uma arquitetura de N camadas, separarando as responsabilidades em CommandStack e QueryStack.
A QueryStack é uma camada síncrona que recupera os dados de um banco de leitura desnormalizado.
O CommandStack por sua vez é potencialmente assíncrono.
O CommandStack segue uma abordagem behavior-centric onde toda intenção de negócio é inicialmente disparada pela UI como um caso de uso.
Utilizamos o conceito de Commands para representar uma intenção de negócio.
Os Commands são declarados de forma imperativa (ex. Transaction) e são disparados assincronamente no formato de eventos, são interpretados pelos CommandHandlers e retornam um evento de sucesso ou falha.
Toda vez que um Command é disparado e altera o estado de uma entidade no banco de gravação um processo tem que ser disparado para os agentes que irão atualizar os dados necessários no banco de leitura.
A imagem do lado esquerdo representa uma aplicação padrão.
A imagem do lado direito representa uma aplicação CQRS.
A implementação do CQRS quebra o conceito monolítico clássico de uma implementação de arquitetura em N camadas onde todo o processo de escrita e leitura passa pelas mesma camadas e concorre entre si no processamento de regras de negócio e uso de banco de dados.
Este tipo de abordagem aumenta a disponibilidade e escalabilidade da aplicação e a melhoria na performance surge principalmente pelos aspectos:
-
Todos comandos são assíncronos e processados em fila, assim diminui-se o tempo de espera.
-
Os processos que envolvem regras de negócio existem apenas no sentido da inclusão ou alteração do estado das informações.
-
As consultas na QueryStack são feitas de forma separada e independente e não dependem do processamento da CommandStack.
-
É possível escalar separadamente os processos da CommandStack e da QueryStack.
Uma outra vantagem de utilizar o CQRS é que toda representação do seu domínio será mais expressiva e reforçará a utilização da linguagem ubíqua nas intenções de negócio.
Toda a implementação do CQRS pattern pode ser feito manualmente, sendo necessário escrever diversos tipos de classes para cada aspecto, porém é possível encontrar alguns frameworks de CQRS que vão facilitar um pouco a implementação e reduzir o tempo de codificação.
Abaixo arquitetura sugerida para resolução do desafio de uma forma mais escalável.
Essa arquitetura trabalha diretamente com microserviços, fila e event-driven.
Todas as api´s rest podem ser utilizadas com Swagger ou através do projeto de testes unitários.
- Link para acessar as api´s https://localhost:44338/swagger/index.html