Arrange Act Assert

Jag Reehals thinking on things, mostly product development

Tag: architecture

13 posts tagged “architecture”.

fn(args, deps) Is Not Anti-Mock

23 Mar 2026

People hear "don't use vi.mock" and assume the alternative is "don't mock anything." That is not the argument.

fn(args, deps) is not anti-mock

The real distinction is simpler: mock explicit collaborators, not implicit imports.

Read More →

What `fn(args, deps)` actually gives you in DDD-style TypeScript

18 Mar 2026

Domain-Driven Design comes with a lot of vocabulary: aggregates, repositories, domain services, bounded contexts, ubiquitous language, anemic models.

That vocabulary can make DDD sound heavier than it really is.

The useful idea is simpler: keep domain behavior and domain boundaries central, and keep infrastructure, persistence, and framework wiring secondary.

fn(args, deps) does not do that modeling for you. What it gives you is a clear shape for application-layer code in TypeScript: one place for use-case input, one place for collaborators, and less room for domain decisions to drift into wiring.

fn(args, deps) and Domain-Driven Design

Read More →

How fn(args, deps) supports SOLID-style design in TypeScript

18 Mar 2026

A lot of developers learn the SOLID principles through class-heavy examples.

That is probably why the conversation so often gets stuck there.

People start to associate SOLID with inheritance hierarchies, interface forests, service classes, and object-oriented ceremony.

But the useful part is not the ceremony.

It is the design pressure.

fn(args, deps) and SOLID principles

One of the simplest ways to apply that pressure in plain TypeScript is this shape:

fn(args, deps)

Where args is the input for this call and deps is the set of collaborators the function needs. You can read that as: data in, capabilities in.

fn(args, deps) is not a replacement for SOLID. It is a simple function shape that makes several SOLID ideas easier to apply without forcing you into class-heavy design.

Read More →

fn(args, deps) Is Programming to Interfaces — And That's How You Control Nondeterminism

18 Mar 2026

Many software systems fail for one very boring reason.

Not because of microservices. Not because of monoliths. Not because of whatever methodology war is trending this week.

They fail because they are unpredictable.

If you make a change and you cannot reliably determine the impact, you cannot safely evolve the system. And when you cannot evolve it, it starts behaving like legacy.

Determinism is the bridge between "works on my machine" and "works every time, everywhere."

fn(args, deps) gets you there — not because it is a clever trick, but because it makes boundaries explicit. Your logic programs to interfaces, which is what lets you control sources of nondeterminism.

Read More →

fn(args, deps) and Composition — Know When to Nest the Dolls

18 Mar 2026

Just because two pieces of code look the same does not mean they are the same.

The most common architecture mistake is not too little abstraction. It is too much, too early. You see duplication, you extract a shared module, and six months later that module is a monster held together by special cases and boolean flags.

Dan Abramov gave a talk about this called The Wet Codebase. The core argument: the wrong abstraction is far more expensive than duplication. Once an abstraction exists, it creates inertia. Nobody wants to be the person who suggests copy-paste.

fn(args, deps) changes this calculus. It makes abstractions cheap to create, cheap to test, and cheap to undo.

When a function's deps grow too large, that can be a signal that some responsibility has stabilized into its own function — and that new function itself follows fn(args, deps). (This is basically SRP pressure showing up in your signature; see the SOLID post for that framing.)

Russian dolls. Each layer independently testable. Each layer reversible.

fn(args, deps) is all you need

Read More →

fn(args, deps) and Hexagonal Architecture

17 Mar 2026

Hexagonal architecture often gets introduced with a lot of diagrams, vocabulary, and ceremony.

Ports. Adapters. Application core. Inbound and outbound boundaries.

That framing is useful, but it can also make the idea feel more abstract than it really is.

fn(args, deps) and hexagonal architecture

You may also hear this called "ports and adapters" — same idea, different name. The port is what a function needs; the adapter is what fulfils it.

A simpler way to understand it is this:

Hexagonal architecture is mostly about keeping business logic independent from delivery and infrastructure details.

One practical way to do that in plain TypeScript is this shape:

fn(args, deps);

Where args is the input for this use case, and deps is the set of external capabilities it needs. You can read that as: data in, capabilities in.

In plain TypeScript, fn(args, deps) is a simple way to implement the core idea of hexagonal architecture without turning the pattern into ceremony.

Read More →

Composition Roots and fn(args, deps)

16 Mar 2026

A lot of developers hear "dependency injection" and immediately think of containers, decorators, registration APIs, lifecycle scopes, and framework magic.

That reaction is understandable.

But that association often leads people to overcomplicate a problem that has a much simpler starting point.

Composition roots and fn(args, deps)

At its core, dependency injection just means this:

Pass collaborators in explicitly instead of reaching for them implicitly.

One of the simplest ways to do that in plain TypeScript is this shape (introduced in the series starting point: fn(args, deps)):

fn(args, deps)

Where args is call-specific input and deps is the set of collaborators the function needs.

You can read that as: data in, capabilities in.

fn(args, deps) is flexible enough to support composition, testing, and clean application wiring without forcing you into a DI framework.

Read More →

Whose Responsibility Is It Anyway? Defining Boundaries with Event-Driven Architecture

20 Jan 2025

Defining clear boundaries is essential to building clean, scalable, and reliable architectures.

In my experience, organisational demands often override the focus on boundaries and domain-driven design. This reflects the tension between following technical best practices and delivering business outcomes quickly. While theoretical approaches are widely discussed at conferences, in books, and in videos, the practical implementation of these ideas is often shaped by cultural dynamics, resource constraints, tight deadlines, and internal politics.

In this post, we'll explore the challenges of maintaining boundaries and potential solutions, using a payment system as an example. This system facilitates transactions between clients and payment providers, such as PayPal or Stripe, highlighting the distribution of responsibilities within its architecture.

Read More →

What About Using Structured Naming for Event-Driven Messaging?

20 Nov 2024

It's a well-known adage that naming things is hard.

In event-driven architectures, a consistent naming convention is essential for scalability, communication, collaboration and maintainability.

At the moment, I'm architecting a new event-driven solution, and I planning to adopt a Structured Naming approach for event types.

In this post I'll share my thoughts on this approach and its benefits.

Read More →

Sometimes Boring is Best

26 Oct 2024

As I stood in the rain as a volunteer race marshal at my local park run, it occurred to me that I wanted the same thing when running applications in production, and that's absolutely nothing to happen.

The last thing I wanted to do was be a hero.

Me in the rain volunteering for Parkrun

🎸 "Always keep on the right side of the path" – the Parkrun version of Monty Python's song.

I'd rather everyone enjoy a safe, smooth race where I can cheer and encourage people on as they pass, reminding them to keep to the right so they don't collide with runners coming the other way.

It's the same in the land of IT. The only thing I want to see when viewing Grafana dashboards is a sea of green, 200 status codes and steady traffic patterns.

Read More →

Don't Look Back in Anger: Let's Design a Better Ticketing System

31 Aug 2024

Saturday's Oasis ticket sales left thousands of fans disheartened after spending hours in virtual queues. As reported by the BBC and echoed by fans on X (formerly Twitter) and Reddit, the experience could be aptly described using a quote from Liam Gallagher himself:

Like many others, I have long been frustrated with platforms like Ticketmaster, See Tickets, and Gigs and Tours. This weekend, their reputations took another hit as fans faced inflated prices and technical glitches.

This made me think if I could design a better, more reliable ticketing system that prioritises fairness and user experience.

Let's not make Sally wait any longer than she has to.

Read More →