Skip to content
View PabloMickaelQuevedo's full-sized avatar
🐱
Senior Software Engineer · Technical Leader
🐱
Senior Software Engineer · Technical Leader

Block or report PabloMickaelQuevedo

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse

Pablo Mickael Quevedo

Senior Software Engineer
.NET · distributed systems · engineering with AI

pablomickaelquevedo.com

.NET 10 C# Clean Architecture, DDD, CQRS


I spent 8 years and 8 months assembling product on a factory line before I wrote my first system. Today one of the systems I designed monitors an operation like that one. Coming from inside an operation did not hand me the rules of that process, but it changed the questions I ask before designing.

The rest is in the portfolio, pablomickaelquevedo.com, in Portuguese: the systems I work on, how I work, and a section on what I don't know yet.


Open Source

Five .NET libraries I write and maintain. This is the part of my work anyone can read today.

Package What it solves Version Downloads
PMQ.Notifications Broken business rules as accumulated data instead of exceptions NuGet Downloads
PMQ.Mediator CQRS mediator with pipeline behaviors, streaming and FluentValidation NuGet Downloads
PMQ.ErrorHandling Centralized ASP.NET Core error handling with RFC 9457 responses NuGet Downloads
PMQ.Identity Provider-agnostic authentication: external OIDC or self-issued JWT NuGet Downloads
PMQ.Domain DDD building blocks: entities, aggregate roots, value objects, domain events NuGet Downloads

They are not five unrelated utilities

They snap together into one request lifecycle, and the fit is the thesis:

flowchart TB
    REQ(["HTTP request"]) --> ID

    subgraph EDGE [" Edge "]
        ID["PMQ.Identity<br/><i>who is calling</i><br/>OIDC or self-issued JWT"]
    end

    ID --> MED

    subgraph APP ["&nbsp;Application&nbsp;"]
        MED["PMQ.Mediator<br/><i>dispatches the use case</i>"]
        VAL{"validation<br/>pipeline"}
        HND["handler<br/><i>one use case</i>"]
        MED --> VAL
        VAL -->|"valid"| HND
    end

    subgraph DOMAIN ["&nbsp;Domain&nbsp;"]
        DOM["PMQ.Domain<br/><i>entity · aggregate<br/>value object · event</i>"]
        NOT["PMQ.Notifications<br/><i>the rule complains here,<br/>accumulating</i>"]
        DOM -->|"rule broken"| NOT
    end

    HND --> DOM
    VAL -.->|"reads"| NOT
    VAL -->|"invalid:<br/>never executes"| ERR

    subgraph OUT ["&nbsp;Output&nbsp;"]
        ERR["PMQ.ErrorHandling<br/><i>standard response</i><br/>RFC 9457"]
    end

    HND --> OK(["200 · result"])
    ERR --> PROB(["4xx · problem details<br/>with every error"])

    classDef pkg fill:#512BD4,stroke:#512BD4,color:#fff
    classDef plain fill:transparent,stroke:#888,color:#888
    class ID,MED,DOM,NOT,ERR pkg
    class VAL,HND plain
Loading

The notification the domain accumulates is the same one the mediator pipeline reads before executing, and the same one the error handler turns into an HTTP response. Domain events publish after the commit: a handler must never observe a fact the transaction rolled back.

The design decision behind them

A violated business rule is an expected outcome, not exceptional control flow. Entities accumulate their failures instead of throwing, which costs less and reports every problem at once instead of only the first:

var order = Order.Create(request.Items);          // never throws

if (order.IsInvalid)
{
    notificationContext.AddFrom(order, NotificationType.BusinessRule);   // → HTTP 422
    return Guid.Empty;
}

Exceptions stay where they belong: programming errors.

What the client gets back

One request, every broken rule, instead of discovering them one deploy at a time:

{
  "title": "A business rule validation failed.",
  "status": 422,
  "errors": [
    { "field": "Items", "message": "Item must be at most 200 characters." },
    { "field": "Items", "message": "Provide at least one item." }
  ],
  "traceId": "00-079d247677d55f7d8427fd421daec5f0-a773dd27f85d9bdb-01"
}

Written after solving the same problem more than once, never on the expectation of reuse: an abstraction built in anticipation is almost always wrong. The whole suite, package by package, is at pablomickaelquevedo.com/open-source.


Outside Work

Two games, both built for family rather than for a store. They are here for what they prove.

Onde está o Gato: 2D Android game, Godot 4 and C#. The game rules do not know the engine: the domain is a pure C# library with no line of Godot in it, and the engine project is a thin layer that reads it and draws it. Most of the game is testable without a GPU, and the whole suite runs headless in seconds. That matters, because I develop on a dual-core Pentium G4560, where opening an Android emulator to check a progression rule is not viable in practice, and what is not viable in practice nobody checks.

Reino do Ritmo: musical game for Roblox, in Luau. Same decision, different stack: the core is pure Luau with no game, workspace, Instance or task, holding economy, rarity rolls, offline income and streaks. Keeping it pure took three layers: a linter configured against the standard library instead of Roblox's, a CI guard against os.time, tick and math.random, and the suite running outside Roblox, which is the one that actually proves it.

The same architectural decision surviving C#/Godot and Luau/Roblox is the best evidence I have that the principle does not depend on the stack. What did not translate is documented with the same honesty: identity, repositories and domain events gain nothing in a single-aggregate save file, and pretending otherwise is how pattern translation turns into cargo cult.


Portfolio LinkedIn Email NuGet

Novo Hamburgo, RS · Brazil

Pinned Loading

  1. PMQ.ErrorHandling PMQ.ErrorHandling Public

    A comprehensive ASP.NET Core error handling library that provides standardized error responses, automatic exception filtering, and business rule notification processing with built-in localization s…

    C# 2

  2. PMQ.Notifications PMQ.Notifications Public

    A package for managing notifications and business rules in .NET applications, making it easy to handle messages, errors, and domain rules in a centralized and strongly-typed way. Designed to fit CQ…

    C# 2

  3. PMQ.Identity PMQ.Identity Public

    Provider-agnostic identity module for authentication and authorization in ASP.NET Core applications. Supports External (OIDC/JWT — e.g. Keycloak, Auth0) and Local (self-issued JWT — e.g. ASP.NET Id…

    C# 1

  4. PMQ.Mediator PMQ.Mediator Public

    A lightweight mediator pattern implementation for .NET applications with FluentValidation integration, request/response pipeline behaviors, streaming support, and notification publishing.

    C# 1

  5. PabloMickaelQuevedo PabloMickaelQuevedo Public

  6. PMQ.Domain PMQ.Domain Public

    Domain-Driven Design building blocks for .NET: entities with identity-based equality, aggregate roots, value objects and domain events. Validation is accumulated as notifications instead of thrown …

    C#