Building a Modular Decoupled Backend using a Monorepo
Tags: monorepo, decoupled, koa
Published: 2024-07-16 11:55:36

There are a lot of articles about why you should decouple your backend to move faster, but very few content on how to practically do it.
This article is a practical approach that we use at Woovi to decouple our backend endpoints breaking them into packages in a monorepo. 
As we scale we can easily convert them to their own microservices.

## Creating a decoupled backend

![Decoupled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ed21phwe2timyvr3zmfu.png)

We should first start designing what endpoint patterns we are going to expose.
In the image above we have five patterns: `/login`, `/register`, `/home`, `/api`, and `/provider`.
You can think about each of these patterns as separate services, but we will deploy them in a single service to simplify deployment and maintenance.

For each endpoint pattern, we are going to create a package in our monorepo, and each package will have its own Koa Router.

The monorepo for these 5 "services" will be like this:

```
packages
- login
- register
- home
- api
- provider
```

A sample koa router code is described below:

```ts
import Router from '@koa/router';

export const getApiRouter = () => {
  const apiRouter = new Router();

  apiRouter.all('/api/status', statusMiddleware);
  apiRouter.get('/api/v1/charge', chargeGet);
  apiRouter.post('/api/v1/charge', chargePost);
  apiRouter.get('/api/v1/transaction', transactionGet);
  apiRouter.post('/api/v1/transaction', transactionPost);
  
  return apiRouter;
}
```

Only using a Koa router is not enough to expose these endpoints.
We need a Koa app to make this possible.

We will create a new package to combine all these "services" endpoints and expose them.

```
packages
...
- woovi-server
```

It will need an `app.ts` file that has the Koa app with some middleware and will combine all these "services" endpoints:

```ts
const app = new Koa({
  proxy: true, // we are behind a proxy
  keys: [config.JWT_KEY],
});

//Add your middleware here
app.use()

// combine all routes here
const apiRouter = getApiRouter();
const homeRouter = getHomeRouter();

app.use(apiRouter.routes()).use(apiRouter.allowedMethods());
app.use(homeRouter.routes()).use(homeRouter.allowedMethods());
```

Then you can run the server

```ts
(async () => {
  const server = createServer(app.callback());
  
  server.listen(config.SERVER_PORT)
})();
```

This way each "service"/package keeps its code separate, following the domain-driven design.

## Breaking in microservices

As you scale, you can decide to move to microservices. 
Instead of using a single server to serve all your endpoints, you can have many servers.
If you follow the approach above, it is as simple as creating a new Koa app for each package that you want to decouple in a new service.

## In Conclusion
This approach lets you decouple your backend, making it clear the boundary of each separate "service" without all the burden of a full microservices approach. You get the benefits without the drawbacks. 

You can read more about DDD here [Domain Driven Design using a Monorepo](https://dev.to/woovi/domain-driven-design-using-a-monorepo-afg)

---
Woovi
[Woovi](https://www.woovi.com) is a Startup that enables shoppers to pay as they like. Woovi provides instant payment solutions for merchants to accept orders to make this possible.

If you want to work with us, we are [hiring](https://woovi.com/jobs/)!