Personal Project Overview — Backend, Part 2

Martin Mička
4 min readApr 24, 2023
Photo by Sean Pollock on Unsplash

Welcome back to my personal project overview series. In the first part of this series, I provided an overview of the backend of my application, including my reasoning for implementing domains with a presentation layer. In this part, I will dive deeper into my reasoning behind the business logic and data layer.

Business Logic

The system is split into the Domains, where the business logic (alongside with data layer) is. I have a bunch of handlers that take data from controller (at this point we can assume that the data are valid) and do additional checks and preparations with persist logic. I’ve chosen Mongo and Prisma ORM for this app. Prisma integrates nicely into Nest, and supports Mongo also pretty well. What I like about Prisma is the clear separation of schema from data mapping, and data classes / object representation. Prisma also has its own agnostic query logic, which I don’t mind, but I can see that some people wouldn’t prefer it. I won’t dive deep into this topic in this post, but Prisma has documented concepts with reasoning on their website, feel free to check it out.

When it comes to Mongo as my choice, I wanted to try building the whole app on NoSQL technology. As someone who uses relational databases most of the time for data store, this was quite a change. Mongo is great for building stuff, as it’s not as restricting when it comes to schema, but I miss relational modeling and it took time to get used to some quirks that NoSQL brings, like data duplication in documents. Of course, I’ve tried embedding documents and other deduplication techniques, but I’m still not sure whether they’re worth it.

Let’s take a look at the core domain. I primarily split logic into two patterns that separate read and write / mutation concerns. Classes that mutate (store or modify domain data) are called handlers. For strictly fetching data, if needed, I use facade pattern. I’m careful with that one, as facades can turn big and ugly real quick, when not thought out properly. I also adapt a repository pattern as the deepest layer in my domains. Repositories are responsible for querying and retrieving data from database (or some other low-level storage / system in the application).

Handler for Groups

Here’s how such handler in my application looks like — this one’s responsible for creating / updating User Group. Every Group has owner and members, with rule that only owners can update groups. Let’s break it down a bit.

Because of granularity and layering, there are arguments that have already been validated — DTO validation was invoked by Nest before the app got to the controller, and the same goes for groupId and ownerId — because this handler is accessed via protected routes, we are certain that the User is authenticated. We also validated beforehand, whether groupId is at least correct ObjectID.

On this layer, business logic and data processing is added — let’s consider update method — Group is prefetched (and therefore checked whether it even exists, method also searches by owner), and then the Group is updated with new data (and members).

I’m also using a Repository pattern for a little bit more granularity as well — in case I wanted to change data logic (e.g. adapt other data sources than my database and Prisma), and I also wanted to separate query / mutation logic from business logic. This way, data layer can be more reused and is overall more modular. It’s also easier to mock and test business logic this way. Tests for updateGroup method within the handler look like this:

Tests for Group Handler

Repository for this part of application is pretty straightforward as well. On this level, I have some basic conditions that only influence how Prisma queries look like. It’s too deep for any business logic or data validation. On this level, it’s appropriate to solve database-related errors and query logic only. My group repository looks like this:

Group Repository

When there’s more logic, I usually build repositories not around an entity, but rather against a specific use-case of entity. For example, when I have an entity User that’s used in general sense, I have multiple facades / repositories for any given use case. I’m also not very fond of leaking repositories from domains into Presentation layer (controllers, high-level services), but for some use cases, it’s a big overhead to introduce some sort of pattern between repository and controller. My personal app is too small for appreciating such separation of concerns.

And that’s how I made this (simple) backend. My takeaway from it is that Nest is solid framework, that offers a lot of possibilities and is a bit more opinionated than traditional Express framework. With this, I only scratched a surface — Nest is very capable and does much more than this. If you want to know more, read Nest documentation and try it on your own, I can nothing but recommend it. If you want to see the whole app, it’s on my Github.

In my next article, I’ll share my insights of project infrastructure and server environment.

--

--

Martin Mička

I’m 27 years old, working as a Software Developer, mainly interested in new technologies and psychology.