ASM Library

The Idea

I'm a big believer in code reuse. Every personal project I've ever started begins with the same fifty lines of plumbing: Serilog, problem details, the same null-checking exceptions, a metadata endpoint, OpenAPI configuration, the three extension methods on IServiceCollection I always end up writing. After the third or fourth project

I got tired of copy-pasting it and started pulling the common bits into a NuGet package.

ASM is the result. It's a set of packages under the Asm.* namespace that bundle up the bits I reach for on every project. Drop them in, call a couple of extension methods, and you've got a working web app with logging, telemetry, OAuth, error handling and a sensible folder structure. Parts of it are deliberately opinionated, but the

opinionated parts are designed to be separable from the generic ones, so you can take what suits you and leave the rest.

Why a Personal Library

I've worked on a lot of codebases where every team has its own way of doing the basics. One project bootstraps the host one way, the next does it slightly differently, the third has copy-pasted bits from the second and quietly broken something along the way. Multiply that across a dozen apps and you've got a maintenance burden

nobody really owns.

The point of ASM isn't to be the best library on NuGet. It's to be consistent. When I sit down to a new app, I want to be writing actual business logic within minutes, not the kind of glue code that's annoying to write twice. Every line of that glue is a line that can rot, drift between apps and accumulate subtle bugs. Centralising

it means I fix something once and every consumer benefits.

What's Inside

Asm

The base package. .NET extension methods I find myself wanting on every project, plus a small family of exception types (NotFoundException, ExistsException, NotAuthorisedException) that map cleanly onto HTTP status codes. The exceptions are deliberately boring. I'd rather throw a NotFoundException at a service boundary and let

middleware turn it into a 404 than scatter status code logic through the domain layer.

Asm.AspNetCore and the Modules pattern

This is the part I'm most pleased with. Most apps I've worked on start as a monolith, then someone tries to split them into microservices, and a year later you have a distributed system with all the complexity of microservices and very few of the benefits. The Modules pattern is a middle ground. You write your app in clearly

separated modules that own their own endpoints and services, but they live in the same process. If a module ever needs to be split out, the seams are already there. If it doesn't, you've avoided paying the network and operational tax.

Asm.Cqrs

A thin layer on top of MediatR for CQRS. Asm.Cqrs.AspNetCore takes it further and binds API endpoints directly to commands and queries, so you skip the controller-as-translator step entirely. You write the handler, you map the route, the request body becomes the command. There's a real productivity win here, and it still surprises

me how clean the resulting code reads.

Asm.Domain and Asm.Domain.Infrastructure

The DDD plumbing. Interfaces and base classes for entities, aggregate roots, repositories and unit of work, plus an EF Core implementation that turns DbSets into queryable specifications and dispatches domain events on save. None of this is groundbreaking on its own, but having it sat in a NuGet package means I don't reinvent the

same patterns each time, and the quirks have already been ironed out by use.

Asm.Serilog, Asm.Hosting, Asm.OAuth

The cross-cutting concerns. Serilog wired to Seq, hosted application bootstrapping, OAuth configuration. One line of code to opt in, sensible defaults if you don't override anything. Nothing exciting, which is exactly the point.

Asm.Reqnroll and Asm.Testing

Test helpers and BDD step definition fixtures. I've leaned hard into Reqnroll for behaviour testing, and these packages take the rough edges off setting it up.

Engineering Principles in Practice

A stable public surface

Once a type is public, breaking it has a cost. ASM uses semantic versioning and I treat breaking changes as an event, not a routine. When I want to retire something, I mark it [Obsolete] first, give consumers a release or two to migrate, and only then remove it. A recent example: the security headers middleware. Rather than ripping

it out, I marked the old overloads [Obsolete], wrote a bridge to a new NetEscapades-based implementation, and let the old API keep working until the next major version.

Tests close to behaviour

Each package has its own test project. BDD specs in Reqnroll for anything user-facing, focused unit tests for serialisation and algorithmic bits. The tests double as living documentation for how I expect the library to be used.

Documentation lives with the code

Every package has its own README. Anything surprising or non-obvious gets explained close to where the code lives, not in a separate documentation site that gets out of date the moment something changes.

Eat your own dog food

This site runs on ASM. So does every other web project I've built in the last few years. The ergonomics get a real workout in production apps, which means the rough edges surface quickly and get filed off.

Why It Matters

None of this is rocket science. What I care about is bringing the same engineering judgement to a hobbyist NuGet package that I would bring to a production codebase at scale. Versioning discipline, consistent patterns, clear seams between concerns, tests as a safety net for change. These are the things that make a codebase pleasant

to work in five years from now, and they're the things I look for when I'm reviewing the work of teams I lead.