Senior Software Engineer
.NET · distributed systems · engineering with AI
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.
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 | ||
| PMQ.Mediator | CQRS mediator with pipeline behaviors, streaming and FluentValidation | ||
| PMQ.ErrorHandling | Centralized ASP.NET Core error handling with RFC 9457 responses | ||
| PMQ.Identity | Provider-agnostic authentication: external OIDC or self-issued JWT | ||
| PMQ.Domain | DDD building blocks: entities, aggregate roots, value objects, domain events |
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 [" Application "]
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 [" Domain "]
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 [" Output "]
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
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.
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.
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.
Novo Hamburgo, RS · Brazil

