DEV Community

Cover image for Why I Built Ironic: Bringing Enterprise Application Architecture to Rust Without Hiding Rust
Morshedul Munna
Morshedul Munna

Posted on

Why I Built Ironic: Bringing Enterprise Application Architecture to Rust Without Hiding Rust

Why I Built Ironic: Bringing Enterprise Application Architecture to Rust Without Hiding Rust

Rust already has amazing web frameworks.

So why build another one?

It's a fair question.

When I started working with Rust professionally, I wasn't looking for a new HTTP framework.

Axum was already excellent.

Tokio handled asynchronous execution beautifully.

Tower provided an elegant middleware ecosystem.

SQLx gave me compile-time checked queries.

The ecosystem wasn't missing another router.

What I kept missing was application architecture.

After building backend systems ranging from small REST APIs to distributed microservices, I noticed something interesting.

Every new project started differently.

Every mature project eventually looked almost identical.


The Hidden Cost of Starting From Scratch

Creating a new Rust API is easy.

Creating a backend that survives three years of development is a completely different challenge.

The first week usually looks like this:

src/
 ├── main.rs
 ├── routes.rs
 ├── handlers.rs
 ├── services.rs
 └── models.rs
Enter fullscreen mode Exit fullscreen mode

Simple.

Clean.

Everyone is happy.

Three months later...

src/
 ├── auth/
 ├── users/
 ├── orders/
 ├── billing/
 ├── notification/
 ├── middleware/
 ├── config/
 ├── telemetry/
 ├── cache/
 ├── jobs/
 ├── events/
 ├── permissions/
 ├── validation/
 ├── metrics/
 ├── errors/
 └── ...
Enter fullscreen mode Exit fullscreen mode

Now the difficult questions appear.

  • Where should dependencies live?
  • How should features communicate?
  • Where does authentication belong?
  • How do background workers access shared services?
  • How should modules be organized?
  • How do we avoid circular dependencies?
  • How do multiple developers work without stepping on each other?

None of these problems are about HTTP.

They're architecture problems.


Rust Gives You Building Blocks, Not a Building

One of Rust's greatest strengths is that it stays out of your way.

It doesn't force a project structure.

It doesn't prescribe dependency injection.

It doesn't dictate application architecture.

That's fantastic for flexibility.

It's less fantastic when five engineers each organize the project differently.

The Rust ecosystem intentionally provides powerful primitives.

But enterprise applications usually require conventions.

Without conventions, every team reinvents the same solutions.


Every Company Eventually Builds Its Own Framework

After enough projects, I noticed a pattern.

Every backend team slowly accumulates infrastructure.

Week after week, project after project, they build:

  • Configuration loading
  • Dependency wiring
  • Authentication
  • Authorization
  • Validation
  • Error handling
  • Health checks
  • OpenAPI
  • Metrics
  • Logging
  • Background jobs
  • Event publishing
  • Testing utilities

Eventually they stop writing business logic.

They're rebuilding their internal platform.

I've seen this happen repeatedly.

The framework simply lives inside the company's repositories instead of being published.

I decided to make mine public.

That's how Ironic started.


I Didn't Want to Replace Axum

One misconception about Ironic is that it's trying to compete with Axum.

It isn't.

Axum is already one of the best HTTP frameworks available.

Instead, Ironic treats Axum as infrastructure.

Think of the stack like this.

Application
     │
 Ironic
     │
  Axum
     │
 Hyper
     │
 Tokio
Enter fullscreen mode Exit fullscreen mode

Axum solves networking.

Ironic solves application organization.

Those are different responsibilities.


Inspired by NestJS, Designed for Rust

Developers coming from Node.js often ask me whether Ironic is "NestJS for Rust."

The answer is both yes and no.

NestJS influenced the developer experience.

It did not influence the implementation.

NestJS relies heavily on:

  • runtime reflection
  • metadata inspection
  • decorators
  • dynamic dependency containers

Rust doesn't have those capabilities.

More importantly...

Rust doesn't need them.

Instead, Ironic uses procedural macros and compile-time code generation where possible, preserving Rust's ownership model instead of replacing it with runtime magic.

The goal was never to make Rust behave like TypeScript.

The goal was to make large Rust applications easier to organize.


Why Modules Matter More Than Routers

In small applications, routing feels like the center of the project.

In large applications, routing becomes almost irrelevant.

Features become the real units of organization.

Instead of asking:

Which file contains this endpoint?

You begin asking:

Which business capability owns this functionality?

That's why Ironic encourages applications to be organized around modules.

Auth
Users
Orders
Payments
Inventory
Notifications
Analytics
Enter fullscreen mode Exit fullscreen mode

Each module owns its own:

  • Controllers
  • Services
  • Repositories
  • DTOs
  • Configuration
  • Events
  • Permissions

This keeps business logic cohesive instead of scattering it across the project.


Dependency Injection Isn't About Magic

Dependency injection has developed a bad reputation in some communities.

Mostly because many frameworks hide everything behind reflection.

I don't think dependency injection is the problem.

Hidden dependencies are.

A service should explicitly declare what it needs.

The framework should simply remove repetitive wiring.

If you still understand ownership, lifetimes, and trait boundaries, then Rust hasn't disappeared.

That's exactly the balance I wanted.


Enterprise Software Isn't Just CRUD

Most tutorials build the same application.

GET /users

POST /users

DELETE /users
Enter fullscreen mode Exit fullscreen mode

Real systems are much more complicated.

A payment might trigger:

  • Database transaction
  • Cache invalidation
  • Event publication
  • Email notification
  • Audit logging
  • Analytics update
  • Background reconciliation

Suddenly you're dealing with distributed concerns rather than HTTP handlers.

Architecture becomes far more important than routing.


Why Compile-Time Matters

One design principle guided almost every decision.

If Rust can verify something at compile time, it should.

Compile-time guarantees make large systems easier to maintain.

They reduce runtime surprises.

They improve refactoring confidence.

They make teams move faster without sacrificing reliability.

Rather than introducing runtime containers or reflection, I wanted Ironic to embrace Rust's philosophy.

Explicit where it matters.

Convenient where repetition exists.


Building for Teams, Not Tutorials

Many frameworks optimize for the first hour.

I wanted to optimize for the third year.

Questions I kept asking myself included:

  • Can ten developers work on this project independently?
  • Will new engineers understand the project quickly?
  • Is each feature isolated?
  • Can modules evolve independently?
  • Does the architecture encourage good boundaries?

Those questions shaped the framework far more than benchmark numbers.


Performance Was Never the Primary Goal

Rust is already fast.

Axum is already fast.

Tokio is already fast.

Saving another few microseconds rarely determines project success.

Developer productivity does.

Maintainability does.

Consistency does.

If a framework helps engineers spend less time wiring infrastructure and more time building business logic, that's often a much bigger win than shaving another 2% off request latency.


Where I Think Rust Backend Development Is Going

The Rust ecosystem is maturing rapidly.

Five years ago, people asked whether Rust was suitable for web development.

Today that's no longer the debate.

The next challenge is improving developer experience for large applications without sacrificing the qualities that make Rust unique.

I believe the future isn't about hiding Rust.

It's about embracing Rust while removing unnecessary repetition.

That's the philosophy behind Ironic.

Not replacing Axum.

Not replacing Tokio.

Not replacing the Rust ecosystem.

Just making it easier to build backend systems that stay maintainable as they grow.


Final Thoughts

Ironic is still evolving, and I don't claim it has all the answers.

But every design decision comes from solving the same problems repeatedly in production systems.

If you've ever found yourself copying the same infrastructure into every new backend project, I'd love to hear your perspective.

Maybe you've solved these problems differently.

Maybe you disagree with some architectural choices.

That's exactly the kind of discussion that helps the Rust ecosystem grow.


Learn More

If you're interested in modular Rust backends, distributed systems, or enterprise application architecture, I'd love your feedback and contributions.

Top comments (0)