DEV Community

Cover image for Why I Flipped Express on Its Head: Rethinking Backends with a Brain-Inspired Neuron/Synapse Pattern
Adam Golan
Adam Golan

Posted on

Why I Flipped Express on Its Head: Rethinking Backends with a Brain-Inspired Neuron/Synapse Pattern

Most backend frameworks organize routes by resource first:

/controllers/users.js     → GET, POST, PUT, DELETE /users
/controllers/products.js  → GET, POST, PUT, DELETE /products
Enter fullscreen mode Exit fullscreen mode

I flipped this entirely. In BEnder, routes organize by HTTP method first:

/methods/GET/users/    → GET /users
/methods/POST/users/   → POST /users
Enter fullscreen mode Exit fullscreen mode

Here's why this seemingly small change unlocks major architectural benefits.


The Problem with Resource-First Organization

When you group routes by resource, you inevitably mix concerns:

// controllers/users.js
class UsersController {
  getAll() { /* read logic */ }
  create() { /* write logic + validation + side effects */ }
  delete() { /* destructive logic + auth checks */ }
}
Enter fullscreen mode Exit fullscreen mode

These methods have vastly different concerns:

  • GET is idempotent, cacheable, safe
  • POST mutates state, triggers webhooks
  • DELETE requires elevated permissions

Bundling them together makes sense from a resource perspective, but creates friction when:

  • You want to apply different middleware per method
  • You need to extract write operations to a separate microservice
  • You're debugging a POST bug buried in a 500-line controller

The Neuron/Synapse Pattern

BEnder uses a brain-inspired architecture:

  • Neurons 🧠 — Containers that auto-discover routes from the filesystem
  • Synapses ⚡ — Endpoint handlers that process specific requests
methods/
├── GET/                   ← Neuron: all read operations
│   ├── users/Users.ts     ← Synapse: GET /users/:action
│   └── products/Prods.ts  ← Synapse: GET /products/:action
├── POST/                  ← Neuron: all write operations
│   └── users/Users.ts     ← Synapse: POST /users/:action
└── DELETE/                ← Neuron: all destructive operations
    └── users/Users.ts     ← Synapse: DELETE /users/:action
Enter fullscreen mode Exit fullscreen mode

Zero manual route registration. Drop a file in the right folder, export a class—done.


The Architecture

// A Synapse is an endpoint handler
export class GET_Users extends Synapse {
    dir = __dirname;

    protected async setRouter(): Promise<void> {
        this.router.get('/:id', async (req, res) => {
            const { code, data } = await this.tryer(async () => {
                return await fetchUser(req.params.id);
            });
            this.responser(res, code, data);
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Key features:

  • tryer<T>() — Async wrapper with automatic error logging
  • responser() — Standardized response formatting (JSON, HTML, text, stream)
  • ready promise — Ensures async route registration completes before server starts

Why Method-First Matters

1. Cleaner Middleware Application

// Apply read-caching only to GET Neurons
server.get(/.*/, cacheMiddleware, getRouter);

// Apply write-validation only to POST Neurons  
server.post(/.*/, validateMiddleware, postRouter);
Enter fullscreen mode Exit fullscreen mode

2. Microservice Extraction

Need to split reads and writes? Your folder structure already reflects that separation:

GET/  → Read replica service
POST/ → Write primary service
Enter fullscreen mode Exit fullscreen mode

3. Cognitive Clarity

When debugging a POST /users/create bug, you go to:

methods/POST/users/Users.ts
Enter fullscreen mode Exit fullscreen mode

Not a 500-line UsersController with 12 methods.


The apps/ Philosophy

Business logic lives outside the routing layer:

BEnder/
├── methods/      ← Thin HTTP routing layer (Neurons + Synapses)
└── apps/         ← Pure business logic (extractable, testable)
    ├── payments/
    ├── notifications/
    └── analytics/
Enter fullscreen mode Exit fullscreen mode

Each apps/ folder is:

  • Importable without HTTP — spawn as child process or worker
  • Extractable — move to its own microservice with copy-paste
  • Testable — unit test pure functions, no Express mocking
// Testing business logic directly
import { calculateTotal } from './apps/payments/service';

test('calculates order total', () => {
  expect(calculateTotal(items)).toBe(99.99);
});
Enter fullscreen mode Exit fullscreen mode

Comparison with Popular Frameworks

Feature Rails NestJS Next.js BEnder
Route organization Resource-first Resource-first File-path Method-first
Auto-discovery Partial
Microservice ready ⚠️ Refactoring ⚠️ Module extraction Native
Child process friendly ⚠️ ⚠️
Unit test simplicity ⚠️ ⚠️ ⚠️

Getting Started

git clone https://github.com/Adam-Golan/BEnder.git my-backend
cd my-backend
npm install
npm run server
Enter fullscreen mode Exit fullscreen mode

Create a new endpoint:

  1. Make a folder: methods/GET/hello/
  2. Create methods/GET/hello/Hello.ts:
import { Synapse } from '../../base';
import { Request, Response } from 'express';

export class GET_Hello extends Synapse {
    dir = __dirname;

    protected async setRouter(): Promise<void> {
        this.router.get('/world', (req: Request, res: Response) => {
            this.responser(res, 200, { message: 'Hello, World!' });
        });
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Visit http://localhost:3000/hello/world

When to Use BEnder

Good fit:

  • API-first backends
  • Projects likely to evolve into microservices
  • Teams that think in HTTP verbs
  • Codebases needing clear read/write separation

Maybe not:

  • Simple CRUD apps where resource-grouping is intuitive
  • Serverless-first deployments (use dedicated frameworks)
  • Projects requiring heavy ORM integration out of the box

Conclusion

BEnder isn't trying to replace Express—it's a layer on top that enforces clean architecture through filesystem conventions.

The Neuron/Synapse pattern emerged from real frustration with tangled controllers and painful microservice extractions. If you've ever spent hours refactoring a monolith or debugging which middleware applies to which route, this approach might resonate.

🔗 GitHub: Adam-Golan/BEnder


Built with ❤️ for developers who believe backends deserve better architecture.


Top comments (0)