Giter Site home page Giter Site logo

cqrsinpractice's Introduction

Welcome to CQRS in Practice

This is the source code for my Pluralsight course CQRS in Practice

How to Get Started

There are 2 versions of the source code: Before and After. The Before version contains the code developed without the CQRS pattern in mind. The After folder is the final version after all refactorings towards CQRS.

In order to run the application, you need to create a database, and change the connection string in appsettings.json.

cqrsinpractice's People

Contributors

ookami-kb avatar seanalexander avatar srihari-venk avatar vkhorikov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cqrsinpractice's Issues

MediatR library

Hey,

Your course on this was really great!

In the introduction of the course you mention that you will introduce the open source library MediatR. Later on you implement your own 'mediator'.
Is there any reason why you prefer your own implementation over MediatR?

Thanks in advance.

Testing Decorator Pattern

Hi

How should we test decorators?
For example for testing the DatabaseRetryDecorator, we should mock ICommandHandler to throws an exception. Right?
But based on your unit testing book and fragile tests, We can use mocks just for unmanaged dependencies.
how to test Decorator pattern?

HTTP Status Codes For Command Failures

Hi Vladimir.

This isn't a technical issue with this repository so much as it is a question for you.

Using a CQRS approach like this repository does, do you have any recommendations on how one would go about responding with different http status codes depending on the nature of a command's failure without API (UI) logic leaking into the application services layer?

Example:

In your StudentController (https://github.com/vkhorikov/CqrsInPractice/blob/master/After/src/Api/Controllers/StudentController.cs) the following method takes in a Student's ID and a StudentEnrollmentDto:

[HttpPost("{id}/enrollments")]
public IActionResult Enroll(long id, [FromBody] StudentEnrollmentDto dto)
{
    Result result = _messages.Dispatch(new EnrollCommand(id, dto.Course, dto.Grade));
    return FromResult(result);
}

What might we do if we wanted to respond with a 404 error if no matching Student is found with the provided ID, but respond with a generic 400 if some other expected failure occurs in the EnrollCommand handler?

Aren't handlers nested in commands a code smell?

I'm just watching your course and one thing bugs me.

In [Segregating Commands and Queries > Commands and Queries in the Onion Architecture], you said that Commands and Queries belong to the core domain layer. That makes sense, since they act as an interface to the core domain layer.

Then in [Implementing Decorators upon Command and Query Handlers > Command and Query Handlers Best Practices] you convert Handlers to nested classes inside Commands.

To my understanding, the handlers do not belong to the core logic. They rather belong to the application services (in your code they even exist in "AppServices" directory). Just looking at their dependencies reveals domain impurity (e.g. SessionFactory).

It feels like you merged the 2 extreme layers from the onion architecture into one.

Could you please explain why? Or maybe, am I missing something?

Testing CommandHandler(s)

Hi Vladimir.

How to test (integration test) a CommandHandler when it is internal ?

1)We can make it public (just for the matter of testing!!!!).

2)Or we can test controller's action methods. If we test controller's method then our tests cover the Messages too (it is good), but on the other hand, we get involved in working with Json in tests and we know that output json format is changed frequently. Besides that Messages uses an IServiceProvider and it is hard to create an instance of it in tests.

What do you suggest?

public sealed class EnrollCommand : ICommand 
    {
        public long Id { get; }
        public string Course { get; }
        public string Grade { get; }

        public EnrollCommand(long id, string course, string grade)
        {
            Id = id;
            Course = course;
            Grade = grade;
        }

        internal sealed class EnrollCommandHandler : ICommandHandler<EnrollCommand>
        {
            private readonly SessionFactory _sessionFactory;

            public EnrollCommandHandler(SessionFactory sessionFactory)
            {
                _sessionFactory = sessionFactory;
            }

            public Result Handle(EnrollCommand command)
            {
                var unitOfWork = new UnitOfWork(_sessionFactory);
                var courseRepository = new CourseRepository(unitOfWork);
                var studentRepository = new StudentRepository(unitOfWork);
                Student student = studentRepository.GetById(command.Id);
                if (student == null)
                    return Result.Fail($"No student found with Id '{command.Id}'");

                Course course = courseRepository.GetByName(command.Course);
                if (course == null)
                    return Result.Fail($"Course is incorrect: '{command.Course}'");

                bool success = Enum.TryParse(command.Grade, out Grade grade);
                if (!success)
                    return Result.Fail($"Grade is incorrect: '{command.Grade}'");

                student.Enroll(course, grade);

                unitOfWork.Commit();

                return Result.Ok();
            }
        }
    }
    public sealed class StudentController : BaseController
    {
        private readonly Messages _messages;

        public StudentController(Messages messages)
        {
            _messages = messages;
        }
        
        [HttpPost("{id}/enrollments")]
        public IActionResult Enroll(long id, [FromBody] StudentEnrollmentDto dto)
        {
            Result result = _messages.Dispatch(new EnrollCommand(id, dto.Course, dto.Grade));
            return FromResult(result);
        }
        
        }
        public sealed class Messages
    {
        private readonly IServiceProvider _provider;

        public Messages(IServiceProvider provider)
        {
            _provider = provider;
        }

        public Result Dispatch(ICommand command)
        {
            Type type = typeof(ICommandHandler<>);
            Type[] typeArgs = { command.GetType() };
            Type handlerType = type.MakeGenericType(typeArgs);

            dynamic handler = _provider.GetService(handlerType);
            Result result = handler.Handle((dynamic)command);

            return result;
        }

        public T Dispatch<T>(IQuery<T> query)
        {
            Type type = typeof(IQueryHandler<,>);
            Type[] typeArgs = { query.GetType(), typeof(T) };
            Type handlerType = type.MakeGenericType(typeArgs);

            dynamic handler = _provider.GetService(handlerType);
            T result = handler.Handle((dynamic)query);

            return result;
        }
    }

Exposing `AuditLoggingDecorator` implementation details in class name

Hi @vkhorikov! I'm a big fan of your PS courses abd blogposts. I often use the course repos as reference when designing my API. Great job, keep doint it!

There is one things that bothers me in this particular repo. Why does AuditLoggingDecorator (or DatabaseRetryDecorator) expose its structure and implementatin details in the class name? The purpose of the first class is to introduce logging behavior. That's it. Looking on the class name, why should I care if it's a decorator or not? Nothing should prevent me from refactoring the class and replacing decorator with, let's say, inheritense from some base class or even copy-pasting from other source (!). Of course, composition is preferable, but why should it go to the class name? Will I have to rename it to AuditLoggingInheritedFromBase or AuditLoggingCopyPaste in case of not using Decorator pattern? Sounds awkward, isn't it?

Wouldn't it be better to have a class called AuditLoggingCommandHandler? This name provides clear understanding of the class purpose and is resistant to inner implementation changes. Any thoughts regarding this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.