Apresentando o Equinox Project

O Equinox Project é um projeto open-source desenvolvido em ASP.NET Core que implementa uma série de tecnologias e abordagens muito utilizadas em grandes projetos.

O Equinox Project é a mais recente contribuição que eu entrego a comunidade técnica e espero que seja de grande ajuda para servir de referência nos futuros projetos escritos em ASP.NET Core.

The Equinox Project

O Equinox Project (versão 1.0) é o resultado de quase duas semanas de estudos e desenvolvimento que dediquei ao criar uma aplicação funcional implementando diversas tecnologias e abordagens na nova plataforma ASP.NET Core.

Por ser totalmente desenvolvido com .NET Core esta aplicação pode rodar em ambientes Windows, Linux e OSX (Mac).

Tecnologias/Recursos Implementados

  • ASP.NET Core 1.1 (com .NET Core)
    • ASP.NET MVC Core
    • ASP.NET Identity Core
      • Isolado do MVC e Autenticando via
        Facebook ou Cadastro
  • Entity Framework Core
  • AutoMapper
  • .NET Core Native DI (Isolado do MVC)
  • Unit of Work
  • Repository e Generic Repository
  • FluentValidator

Arquitetura

  • Arquitetura completa com separação de responsabilidades, SOLID e Clean Code
  • DDD – Domain Driven Design (Camadas e Domain Model Pattern)
  • Domain Events
  • Domain Notifications
  • CQRS (Com consistência imediata)
  • Event Sourcing

Versão 1.0

 O Equinox Project na versão 1.0 implementa um cadastro de clientes (CRUD) com regras de negócio e validações de dados.

  • Toda escrita de dados ocorre através de Commands e CommandHandlers que são processados por um Bus em memória (podendo ser adaptado para um Message Queue por exemplo).
  • Após a execução de um Command é disparado um Evento que realiza alguma ação informativa e também é persistido na base (Event Sourcing).
  • A leitura de dados ocorre de forma mais simples dispensando algumas camadas de negócio.
  • Todas as ações são autorizadas pelo mecanismo do ASP.NET Identity que baseia-se em Claims para permitir a leitura e escrita dos dados.
  • As validações de consistência dos dados são realizadas nos Commands e utilizam o FluentValidator como mecanismo.
  • Todos os erros no processamento ou na validação dos dados são disparados através de Domain Events/Notifications e são informados ao usuário de forma personalizada.
  • É possível visualizar a história da entidade através da aplicação que informa desde a criação até a exclusão as mudanças dos dados que ocorreram e o usuário que as executou.

Estes são alguns dos recursos implementados, existem diversos outros em toda extensão da aplicação. Cada um destes recursos eu irei tratar em artigos individuais em uma nova série sobre ASP.NET Core que irei iniciar muito em breve.

Aviso

  • Este projeto não pretende ser uma solução definitiva para todos os cenários.
  • Algumas versões utilizadas (inclusive do ASP.NET Core 1.1) estão em Beta ou Pre-Release.
  • Cuidado ao utilizar este projeto na sua produção. Analise bem os riscos de ser um Early Adopter.
  • Talvez você não irá precisar de muitos dos recursos implementados, procure evitar o OverEngineering

Sobre o futuro

A versão 2.0 do Equinox Project será uma aplicação bem mais extensa com os recursos a seguir:

  • Aplicação completa de aluguel (Booking) utilizando Domain Model Pattern, CQRS e Event Sourcing.
  • ASP.NET Identity trabalhando através de serviços WebAPI com Bearer Token
  • Novo front-end
  • Bancos separados para leitura e gravação de dados
  • Testes de Unidade

Acompanhe os detalhes que serão atualizados no RoadMap do projeto.

Sugestões?

Tem uma boa ideia sobre implementação ou gostaria de ver algo implementado?
Sugestões e críticas serão muito bem vindas!

Por que Equinox?

O Equinócio (Equinox) é um evento astronômico em que o plano do equador da Terra passa pelo centro do Sol. Este evento ocorre duas vezes por ano em torno de 20 de Março e 23 de Setembro. Wikipedia

Equinox é também uma série de publicações (subtítulo: “The Review of Scientific Illuminism”) em forma de livro que serve como o órgão oficial da A∴A∴, uma ordem iniciática fundada por Aleister Crowley Wikipedia

Estamos Online

O projeto está publicado *orgulhosamente* no Microsoft Azure, experimente!

Código Fonte

The Equinox Project – GitHub


Caso esteja interessado em conhecer mais sobre ASP.NET, DDD, Padrões de Arquitetura como CQRS, Event Sourcing e boas práticas de desenvolvimento não deixe de conferir as ementas dos meus cursos:

Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.

65 pensou em “Apresentando o Equinox Project

  1. Parabéns pela iniciativa, sempre ajudando a comunidade e ensinando a fazer as coisas de modo correto

  2. Incentivando os demais e disseminando conhecimento, muito boa a sua contribuição com a comunidade.

  3. Parabéns Eduardo Pires pela sua iniciativa, como sempre nos ajudando bastante.

    E a iniciativa é bem promissora.

  4. Parabéns Eduardo, este projeto sem dúvida auxiliará muitos desenvolvedores que procuram se aprofundar no DDD. Uma única dúvida, o Equinox aborda a utilização de Bounded Context?

  5. Legal Eduardo.
    Por que não utilizou MediatR para notification?
    Sobre o asp.net identity com bearer token, por que não implementa um server hibrido com identity server? ou até mesmo separado, já que você já separou o asp.net identity
    O que quis dizer com 2 bancos de dados?

    Ficou show o projeto, espero que de continuidade

    • Olá Rodrigo!

      MediatR irei implementar quando os serviços REST fizerem o papel da camada de Application, por hora não achei necessário.

      É uma boa ideia, deixar o projeto preparado para o Identity Server é uma boa.

      No CQRS nós temos bancos de leitura e de escrita separados, seria esta a ideia.

      Abs!

      • Ia ser lindo ter mediatr para notifications e identityserver 😛
        Ah, um cachemanager também seria bom hehe

        Sobre o CQRS, então, ele deixa 1 banco desnormalizado para leitura? e um banco normalizado para escrita

  6. Muito bom o projeto, tenho uma dúvida, o CustomerCommandHandler sería o Service? Como poderia fazer os teste desta classe?

    • Olá Dariel, não seria bem o service, mas atua neste papel também.
      Uma vez que delegamos as ações para os comandos é neste formato que trabalhamos.

      É fácil testar, por ex mockando através do IBus.

  7. Inicialmente Parabéns pelo pelo projeto, está incrível! Agora tenho uma dúvida… Com o EF Core não é mais possível criar uma classe com as configurações das entidades?

      • Eu fiz aqui no meu projeto, da mesma forma que seu outro projeto, quando trabalhava DDD no Framework 4.6

          • Vou passar aqui como fiz, mas a princípio, tudo .NET Core.

            public class BusinessFunctionConfig
            {
            public BusinessFunctionConfig(EntityTypeBuilder businessFunction)
            {
            businessFunction.HasKey(c => c.Id);
            businessFunction.Property(c => c.Id).ValueGeneratedOnAdd();

            businessFunction.Property(c => c.Name)
            .IsRequired()
            .HasMaxLength(100);

            businessFunction.Property(c => c.Code)
            .IsRequired();
            }
            }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
            base.OnModelCreating(modelBuilder);

            new BusinessFunctionConfig(modelBuilder.Entity());
            }

  8. Que código lindo!
    Cara você é o melhor! Eu já vi exemplos conceituais, mas tudo muito incompleto. É nessa hora que separamos os homens dos garotos kkk!

    Parabéns!

  9. Como posso inicializar o:

    InMemoryBus.ContainerAccessor = () => accessor.HttpContext.RequestServices;

    desde um ConsoleApplication, é que estou fazendo alguns testes no projeto e para não criar todo o projeto ASP.Net criei um projeto Console

    Obrigado

  10. Eduardo tenho uma dúvida, você no projeto não esta utilizando o SimpleInjector, porque, tem alguma razão em especial.

    este trecho de código na classe SimpleInjectorBootStrapper como eu posso fazer ele no SimpleInjector?

    services.AddSingleton(Mapper.Configuration);
    services.AddScoped(sp => new Mapper(sp.GetRequiredService(), sp.GetService));

    Cómo devería ser feita a injeção de dependencias do mapper com o SimpleInjector?

    • Preferi o DI nativo para deixar mais “natural” (apesar de ter esquecido a classe com o nome de simpleinjector).

      Dá para fazer com o simpleinjector também, só vai precisar registrar as dependencias do Identity na mão (obrigatoriamente)

      Abs!

  11. Eduardo, eu também tenho uma dúvida, vi que o projeto de Data (os outros não olhei) tu usa o xproj, appsettings.json e project.json, ou seja a estrutura nova do .net core. Mas quando eu crio um novo projeto class library, ele não vem com essa estrutura. Como eu crio um projeto vazio do formato do .net core? Parece bem básico isso mas eu não achei como faz =P. Obrigado

  12. Eduardo,

    Você tem planos de implementar o Angular?

    A interface nova que está no Roadmap seria o Angular Material?

    Você implementou o UoW, mas vi que chamou os contextos diretamente no repositório, é alguma técnica?

    TFA.’.

  13. Olá Eduardo, fiz o download do projeto mais não consigo executar. Aparece a seguinte mensagem da saída:
    O programa “[9868] dotnet.exe” foi fechado com o código -2147450749 (0x80008083).
    O programa “[11952] iisexpress.exe” foi fechado com o código 0 (0x0).

    Podes me ajudar

  14. Parabéns Eduardo! Cara como Sugestão, que tal um artigo sobre Unity of Work?

    Tenho duvidas e tem uma galera que também tem.

    Parabéns

  15. Artigo muito bom! Visto que você pediu sugestões e afirmou sobre a V2. Deixo a minha: “Bancos separados para leitura e gravação de dados” utilizando o DocumentDB para leitura dos dados.

    Forte abraço!

  16. Boa tarde pessoal,

    Será que alguém poderia me ajudar vou descrever meus problemas.

    Tentei abrir o projeto no VS2015 e deu problema. Preciso saber se o projeto pode ser aberto no VS2015 e o que seria preciso para que isso aconteça.

    Depois disso eu abrir o projeto no VS2017 e ele conseguiu abrir o projeto porem nao consigo compilar da o seguinte erro.
    Cannot find project info for… This can indicate a missing project reference

  17. Olá. parabéns pelo projeto….
    Gostaria que abordasse algo relacionado a multitenance, ou como filtrar dados de uma tabela para um determinado usuário.. é possível ?
    Objetivo.: Manter apenas 1 banco de dados…

    Abcs e parabéns novamente

  18. Boa tarde, antes do mais quero agradecer pelos esforço prestado na comunidade Brasileira que acabou se reflectindo aqui em Angola e já somos vários Dev seguindo seus ensinamentos. Graças ao seu mega video sobre DDD consegue aprender perfeitamente o uso do DDD e não pude deixar de reparar o projecto Equinox que esta crescendo e já usando o Asp.net Core 2 e outras Tecnologias interessantes que seria uma grande valia se tivesse uma explicação sobre o Equinox que para mim é uma lenda. Consigo, entender até certo ponto…..

    Em nome da comunidade de Desenvolvedores Angolanos, agradecemos por tudo e não temos palavras para expressar.

  19. This project is very helpful, especially the domain notification implementation. Is there anywhere where the flow of notifications from the handler through the BaseController to the SummaryComponent is explained?
    I’m wondering how the SummaryViewComponents list of notifications gets populated. I am assuming it is being populated from the BaseController list of notifications. Thank you

  20. Boa noite Eduardo! Como vai?
    Estou tentando desenvolver algo para fins de estudo e estou rcebendo o seguinte erro:

    An error occurred while starting the application.
    TypeLoadException: Method ‘Handle’ in type ‘Hermes.Domain.Core.Notifications.DomainNotificationHandler’ from assembly ‘Hermes.Domain.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ does not have an implementation.
    Hermes.Infra.CrossCutting.IoC.NativeInjectorBootStrapper.RegisterServices(IServiceCollection services)

    TypeLoadException: Method ‘Handle’ in type ‘Hermes.Domain.Core.Notifications.DomainNotificationHandler’ from assembly ‘Hermes.Domain.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ does not have an implementation.
    Hermes.Infra.CrossCutting.IoC.NativeInjectorBootStrapper.RegisterServices(IServiceCollection services)
    Hermes.UI.Site.Startup.RegisterServices(IServiceCollection services) in Startup.cs
    +
    NativeInjectorBootStrapper.RegisterServices(services);
    Hermes.UI.Site.Startup.ConfigureServices(IServiceCollection services) in Startup.cs
    +
    RegisterServices(services);
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
    Microsoft.AspNetCore.Hosting.Internal.WebHost.EnsureApplicationServices()
    Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()

    O método em questão está lá pois estou usando o seu código.
    Saberia me dizer qual seria o problema?

  21. Parabéns pela inciativa Eduardo! Muito bom o material! Só uma duvida: no projeto a camada de aplicação conversa direto com o repositório mas nesse vídeo: https://youtu.be/i9Il79a2uBU entre o repositório e a camada de aplicação tem uma camada de serviço. Qual seria a melhor abordagem?

  22. Boa Tarde Eduardo!

    Baixei a nova versão e ao editar um customer ou ver o trackingchanges da erro :

    EquinoxProject/src/Equinox.Infra.Data/Repository/EventSourcing/EventStoreSQLRepository.cs

    public void Store(StoredEvent theEvent)
    {
    _context.StoredEvent.Add(theEvent);
    _context.SaveChanges();
    }

  23. Bom dia Eduardo, ótimo artigo sempre acompanho suas publicações.
    Estou estudando sobre event sourcing e CQRS para aplicar em uma solução, a minha duvida é. Para aplicar consistência eventual usando como bus o Rabbitmq, que mudanças deveria aplicar no projeto?

  24. Eduardo,
    Pode me ajudar! Baixei o código, mas tem 2 projetos que não consegue carregar?
    WEB e API, pode me ajudar?
    Grato.

  25. Olá Eduardo!

    Uma dúvida com relação aos valores de objetos, como eles são representados nas Views Models e commands? Será representado por DTO ou seguiram as estruturas representadas nas tabelas?
    Exemplo:
    public class AddressVO : ValueObject
    {
    public string Street { get; private set; }
    public string HouseNumber { get; private set; }
    public string Locality { get; private set; }
    public string City { get; private set; }
    public string FederationUnity { get; private set; }
    public ZipVO Zip { get; private set; }

    public AddressVO(string street, string houseNumber, string locality, string city, string federationUnity, string zipCode)
    {
    Street = street;
    HouseNumber = houseNumber;
    Locality = locality;
    City = city;
    FederationUnity = federationUnity;
    Zip = new ZipVO(zipCode);
    }
    }

    public class Person : Entity
    {
    public string Name { get; private set; }
    public CpfVO Cpf { get; private set; }
    public AddressVO Address { get; private set; }
    }

    View Model / Command
    public class AddressDTO
    {
    public string Street { get; set; }
    public string HouseNumber { get; set; }
    public string Locality { get; set; }
    public string City { get; set; }
    public string FederationUnity { get; set; }
    public ZipTO Zip { get; set; }
    }
    public class PersonDTO
    {
    public string Name { get; set; }
    public CpfDTO Cpf { get; set; }
    public AddressDTO Address { get; set; }
    }

    View Model / Command (Tabela)
    public class PersonDTO
    {
    public string Name { get; set; }
    public string Cpf { get; set; }
    public string Street { get; set; }
    public string HouseNumber { get; set; }
    public string Locality { get; set; }
    public string City { get; set; }
    public string FederationUnity { get; set; }
    public string Zip { get; set; }
    }

Os comentários estão fechados.