Gallalaus: An Expedition into Full Stack Development - Project Structure
Embark on the next leg of our Full Stack Development expedition with this third post in the Gallalaus series! In this stop, I'll share how I prefer to structure my projects for a smoother journey ahead.
Main project structure
This project will be structured as a monorepo. Initially, I won't be using any specialized monorepo tools aside from pnpm workspace. This choice will help in organizing packages and running commands efficiently. By this, I mean that the entire codebase will reside within a single Git repository.
| ── .gitlab
| | ── merge_request_templates
| | |── merge_request_templates.md
| ── backend
| ── data
| ── frontend
| ── logs
| ── test
| ── README.md
| ── package.json
| ── pnpm-workspace.yaml
| ── pnpm-lock.yaml
| ── CHANGELOG.md
| ── .gitignore
| ── .env.example
| ── .gitlab-ci.yml
| ── backend-build-start-script.sh
| ── docker-compose.yml
Let's break this down quickly:
.gitlab folder houses GitLab configurations, including a customized merge request/pull request description template.
the backend folder holds the monolithic NestJS backend code for now.
the data folder stores MongoDB Docker data.
the frontend folder hosts the Angular app.
the logs folder stores application logs.
the test folder includes API tests.
backend-build-start-script is a Bash script used to build the backend Docker image and launch Docker Compose for the project.
Nestjs proejct structure
│── Dockerfile
│── nest-cli.json
│── package.json
│── pnpm-lock.yaml
│── README.md
│── src
│ │── app.module.ts
│ │── config
│ │── debug
│ │ │── debug.controller.spec.ts
│ │ │── debug.controller.ts
│ │── definitions
│ │── health
│ │ │── health.controller.spec.ts
│ │ │── health.controller.ts
│ │── main.ts
│ │── shared
│ │── tasks
│── test
│ │── app.e2e-spec.ts
│ │── jest-e2e.json
│── tsconfig.build.json
│── tsconfig.json
Let us look closely now at each layer and understand what it contains:
config
│ │── config
│ │ │──config.module.ts
│ │ │── config.service.spec.ts
│ │ │── config.service.ts
│ │ │── env.schema.ts
│ │ │── logger
│ │ │ │──index.ts
│ │ │ │── logger.config.ts
The config folder stores application and third-party library configurations. I prefer the approach of exporting from an index
file, as it keeps things compact and easy to maintain, as seen in the logger folder and throughout this project. This folder follows the classic NestJS module structure, as I appreciate the benefits of this framework. An important aspect to highlight here is the env.schema
file, which serves as a schema mapping for the application's required environment variables. This approach ensures that we always have clarity about our required variables and allows us to validate their availability during the application's bootstrap.
definitions
│ │── definitions
│ │ │── enums
│ │ │ │── index.ts
│ │ │── index.ts
│ │ │── interfaces
│ │ │ │── index.ts
│ │ │ │── tasks.interfaces.ts
│ │ │ │── tasks.repository.interfaces.ts
│ │ │── types
│ │ │── index.ts
│ │ │── tasks.repository.types.ts
The definitions folder is one of the straightforward ones, as it should contain the entire definitions that will be shared between the modules, as we can see these are: types, interfaces, enums, etc. I will not go into much detail here.
shared
│ │── shared
│ │ │── middleware
│ │ │ │── db-logger.middleware.ts
│ │ │ │── logger.middleware.ts
│ │ │ │── trace.middleware.ts
│ │ │── request-context
│ │ │ │── request-context.module.ts
│ │ │ │── request-context.service.spec.ts
│ │ │ │── request-context.service.ts
│ │ │── errors
│ │ │── http
│ │ │ │── exceptions
│ │ │ │── filters
│ │ │ │ │── execption-response.filter.ts
│ │ │── validation-pipes
│ │ │ │── index.ts
│ │ │ │── object-id-validation.pipe.ts
│ │ │── utils
│ │ │ │── utils.module.ts
│ │ │ │── utils.service.spec.ts
│ │ │ │── utils.service.ts
The shared folder, contains, as we can see, all the shared code, that will be used by our business modules. Some important notes here are:
utils
module, should contain utils methods, that are useful in the entire app, like: password hash method, transformation methods, and so on.request-context
module is a module that will allow us to make use of some specific global variables through the application. As an example, we can see the tracing-id of each request.- the
http/filters
will contain useful exception filters that can be applied on top of the http calls. see this exception filters nestjs
tasks-module
│ │── tasks
│ │ │── dto
│ │ │ │── create-task.dto.ts
│ │ │ │── find-many-query.dto.ts
│ │ │ │── index.ts
│ │ │ │── update-task.dto.ts
│ │ │── models
│ │ │ │── task.model.ts
│ │ │ │── index.ts
│ │ │── repository
│ │ │ │── tasks.repository.ts
│ │ │ │── index.ts
│ │ │── tasks.controller.spec.ts
│ │ │── tasks.controller.ts
│ │ │── tasks.module.ts
│ │ │── tasks.service.spec.ts
│ │ │── tasks.service.ts
This module follows the standard Nest module structure. A few important points to note:
- in the dtos folder, DTOs should implement an interface that defines their structure for sharing across the application.
- the repository is an abstract class responsible for accessing the data source. I'll delve deeper into this topic in a dedicated article. This example demonstrates how the application modules will be structured
Angular project structure
For the Angular part, we'll adopt a component-based style. Here's the initial proposed structure:
├── angular.json
├── karma.conf.js
├── package.json
├── pnpm-lock.yaml
├── README.md
├── src
│ ├── app
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── app-routing.module.ts
│ │ ├── components
│ │ │ ├── board
│ │ │ │ ├── board.component.html
│ │ │ │ ├── board.component.scss
│ │ │ │ ├── board.component.spec.ts
│ │ │ │ └── board.component.ts
│ │ │ ├── definitions
│ │ │ │ ├── enums
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── priority.enum.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── interfaces
│ │ │ │ ├── index.ts
│ │ │ │ ├── task.interface.ts
│ │ │ │ └── task-list.interface.ts
│ │ │ ├── navigation
│ │ │ │ ├── header
│ │ │ │ │ ├── header.component.html
│ │ │ │ │ ├── header.component.scss
│ │ │ │ │ ├── header.component.spec.ts
│ │ │ │ │ └── header.component.ts
│ │ │ │ └── navigation.module.ts
│ │ │ ├── task
│ │ │ │ ├── task.component.html
│ │ │ │ ├── task.component.scss
│ │ │ │ ├── task.component.spec.ts
│ │ │ │ └── task.component.ts
│ │ │ ├── task-add
│ │ │ │ ├── task-add.component.html
│ │ │ │ ├── task-add.component.scss
│ │ │ │ ├── task-add.component.spec.ts
│ │ │ │ └── task-add.component.ts
│ │ │ ├── task-list
│ │ │ │ ├── task-list.component.html
│ │ │ │ ├── task-list.component.scss
│ │ │ │ ├── task-list.component.spec.ts
│ │ │ │ └── task-list.component.ts
│ │ │ └── task-list-add
│ │ │ ├── task-list-add.component.html
│ │ │ ├── task-list-add.component.scss
│ │ │ ├── task-list-add.component.spec.ts
│ │ │ └── task-list-add.component.ts
│ │ └── material.module.ts
│ ├── assets
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test.ts
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
For the frontend Angular application, it's a standard Angular app for now. We'll keep the current structure as we explore our development direction and goals.
In the next part of our journey, we'll focus on system design, identifying necessary components, establishing communication between the frontend and backend, designing the database, and working toward achieving the MVP status quo. I'll also aim to provide simple REST API definitions using the OpenAPI specification.
As we conclude this stage of our journey, we're one step closer to mastering Full Stack Development. Stay tuned for more insights and discoveries in the next chapter.
Subscribe to my newsletter
Read articles from Flavius Cojocariu directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Flavius Cojocariu
Flavius Cojocariu
I'm Flavius Cojocariu, a friendly and experienced Software Engineer, based in München, Germany, with a keen focus on crafting amazing web experiences. I believe in embracing life's adventures and constantly seeking opportunities to grow and learn. Let's dive into my story! My work philosophy revolves around constant growth and innovation. I believe that staying curious and adaptable is crucial in an ever-evolving landscape. Through each project, I aim to deliver not just results but also meaningful experiences that resonate with audiences and clients alike. Throughout my career, I've been fortunate enough to contribute to some pretty fantastic projects. From responsive websites that adapt like chameleons to web applications with seamless user experiences, I take immense pride in every line of code I write. Some of the companies I have worked for are part of the IoT/fintech industry, travel industry, and even telecom and market research. I'm excited about the future and the possibilities it holds. I'm open to new collaborations, challenges, and endeavors that push the boundaries of creativity and innovation. Thank you for taking the time to explore my portfolio. If you have any questions, want to discuss potential projects, or simply want to connect, don't hesitate to reach out.