Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5fa0d49
Layered Phase 1 - Articles (#163)
yamcodes Jun 14, 2025
b9e6ea7
Layered Phase 2 - Comments (#169)
yamcodes Jun 20, 2025
443a465
Layered Phase 3 - Tags (#170)
yamcodes Jun 20, 2025
3a7bb5a
Layered Phase 4 - Users (#171)
yamcodes Jun 20, 2025
7903534
Layered Phase 5 - Profiles (#173)
yamcodes Jun 20, 2025
6f79af6
Layered Phase 6 - Misc (#174)
yamcodes Jun 20, 2025
17cb3af
Merge branch 'main' of https://github.com/bedtime-coders/bedstack int…
yamcodes Jun 20, 2025
c86ab52
Update dependencies in bun.lock: bump @types/node to 24.0.3, add cros…
yamcodes Jun 20, 2025
7a19960
Refactor seed.ts to reorganize imports for consistency and clarity, m…
yamcodes Jun 20, 2025
c54d5f4
Remove unused Trunk configuration files and clean up biome.json by el…
yamcodes Jun 20, 2025
f1f791f
Refactor UsersService test file to update import paths for consistenc…
yamcodes Jun 20, 2025
ffdebef
Refactor TagsService test file to update import paths for consistency…
yamcodes Jun 20, 2025
b5c9ecc
Refactor ProfilesService test file to update import paths for consist…
yamcodes Jun 20, 2025
65943bd
Refactor test files for ArticlesService and AuthService to update imp…
yamcodes Jun 20, 2025
b5f964c
Update docs/getting-started.md
yamcodes Jun 20, 2025
10418f5
Update docs/dev-container.md
yamcodes Jun 20, 2025
93df412
Update GOVERNANCE.md
yamcodes Jun 20, 2025
c27e17e
Update following status in toDomain mapper to handle cases where curr…
yamcodes Jun 20, 2025
ab89f69
Merge branch 'layered' of https://github.com/bedtime-coders/bedstack …
yamcodes Jun 20, 2025
955ec3c
Refactor user schema to replace date type with timestamp for createdA…
yamcodes Jun 20, 2025
984cf83
Refactor articles controller to use constants for default offset and …
yamcodes Jun 20, 2025
fa5e553
Fix typo in error message for comment deletion in CommentsService, co…
yamcodes Jun 20, 2025
4ca5f19
Refactor ArticlesRepository to consolidate filtering logic for articl…
yamcodes Jun 20, 2025
53e5305
Update UsersService tests to use Date objects for createdAt and updat…
yamcodes Jun 20, 2025
5279c00
Update drizzle-kit to version 0.31.1, change TypeScript module resolu…
yamcodes Jun 20, 2025
e5715eb
Remove duplicate import of DrizzleQueryError in app.module.ts for cle…
yamcodes Jun 20, 2025
be73c4f
Update updatedAt fields in articles, comments, tags, and users schema…
yamcodes Jun 20, 2025
faa81c4
Refactor test cases in ArticlesService, AuthService, ProfilesService,…
yamcodes Jun 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"password": "postgres"
}
],
"cSpell.words": ["bedstack", "Elysia", "elysiajs", "favicons", "typesafe"]
"cSpell.words": ["bedstack", "Elysia", "elysiajs", "favicons", "typesafe"],
"typescript.tsdk": "node_modules/typescript/lib"
}
161 changes: 161 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Architecture

## Overview

This service uses a modular **Layered Architecture** inspired by the [NestJS philosophy](https://docs.nestjs.com/#philosophy). We favor **separation of concerns** over minimalism to maintain clear boundaries between different parts of the system.

We separate the system into 3 main layers:

1. **Controller** – Talks to the client
2. **Service** – Handles the business logic
3. **Repository** – Interacts with the database

Each domain feature (e.g. `articles`, `profiles`, `tags`) is isolated into its own module, containing these layers plus:

- **Mapper** - Transforms data between layers
- **Schema** - Defines database tables and relations

```mermaid
graph TD
subgraph Controller Layer
C1[articles.controller.ts]
C2[comments.controller.ts]
C3[tags.controller.ts]
end

subgraph Service Layer
S1[articles.service.ts]
S2[comments.service.ts]
S3[tags.service.ts]
end

subgraph Repository Layer
R1[articles.repository.ts]
R2[comments.repository.ts]
R3[tags.repository.ts]
end

subgraph Schema Layer
SC1[articles.schema.ts]
SC2[comments.schema.ts]
SC3[tags.schema.ts]
SC4[article-tags.schema.ts]
end

subgraph Mapper Layer
M1[articles.mapper.ts]
M2[comments.mapper.ts]
M3[tags.mapper.ts]
end

C1 --> S1 --> R1 --> SC1
C2 --> S2 --> R2 --> SC2
C3 --> S3 --> R3 --> SC3

S1 --> M1
S2 --> M2
S3 --> M3

SC1 --> SC4
SC3 --> SC4
```

## Layer Responsibilities

### 1. Controller Layer (Client-facing)

- Receives data from the client (DTO)
- Returns data to the client (DTO)
- Validates data types
- Calls the service layer
- Can shape requests and responses, without performing any business logic

### 2. Service Layer (Business Logic)

- Contains the business logic
- Can perform any kind of calculation or transformation as long as it's part of the business rules
- Validates logic rules (e.g., checking if a user can register)
- Handles errors and logging
- Calls the repository layer to get or save data
- Can receive controller-level DTOs, but must map or validate them before passing data to the repository

### 3. Repository Layer (Database Access)

- Talks to the database
- Only responsible for saving and retrieving data
- **No** assumptions about validation
- **No** business logic should go here
- Handles pagination, sorting, and other database-specific operations
- Returns raw database rows, not domain entities

### Additional Layers

#### Mapper (Data Transformation)

- Transforms Row types from the database to domain entities or DTOs
- Performs camelCase vs. snake_case mapping if needed
- Converts Date to ISO strings for output, etc.

#### Schema (Database Definitions)

- Defines schemas using an ORM (e.g. `pgTable()` with Drizzle ORM and PostgreSQL)
- Optionally defines table relations (e.g. `relations()` with Drizzle ORM)

## Type Design Principles

1. **Interfaces vs Classes**:

- Use interfaces (`IUser`) to define contracts between layers
- Use classes (`User`) for concrete implementations. The (database) entity is a concrete implementation of the interface.
- This separation allows for better testing and flexibility

2. **Canonical Forms**:

- Store canonical forms in the database (e.g., `birthdate`)
- The canonical form is represented in the entity (`User`) _and_ the interface (`IUser`)
- The DTO might use a different form, e.g. `CreateUserDto` might use `age` instead of `birthdate`
- Use mappers to convert between forms

3. **System vs Domain Properties**:
- System properties (`id`, `createdAt`, `updatedAt`) are managed by the base entity
- Domain properties (e.g. `email`, `name`) are defined in the interface, enforced by the entity, and controlled by the DTOs

## Examples

### Example 1: Can register?

```typescript
canRegister(user: Partial<IUser>) {
if (user.email.endsWith('@banned.com')) {
throw new ForbiddenException('Email domain is not allowed');
}

if (!this.isOldEnough(user.birthdate)) {
throw new ForbiddenException('User must be at least 13 years old');
}
}
```

This check lives in the service layer because:

- It's business logic
- It could change based on product decisions
- It might be reused across different controllers (`signup`, `adminCreateUser`, etc.)
- If tomorrow we add GraphQL on top of our REST, this logic will remain the same

### Example 2: Normalize email

```typescript
normalizeEmail(email: string) {
return email.toLowerCase().trim();
}
```

Also clearly service-level: it's a standardized rule, not controller-specific logic.

## See also

- More on **Project structure** - see [Project Structure](PROJECT_STRUCTURE.md)
- **Contributing** - see [Developer's Guide](CONTRIBUTING.md)
- **API Documentation** - see [RealWorld Backend Specifications](https://realworld-docs.netlify.app/specifications/backend/introduction/)
- **Drizzle ORM Documentation** - see [Drizzle ORM](https://orm.drizzle.team/)
22 changes: 11 additions & 11 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
- Focusing on what is best not just for us as individuals, but for the
overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting

## Enforcement Responsibilities
Expand Down Expand Up @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
Expand Down
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Hey there! We're thrilled that you'd like to contribute to this project. Your he

This project uses [Bun](https://bun.sh) as a runtime as well as a package manager. It's a modern, fast, and lightweight alternative to [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/). To install Bun on POSIX systems (like Ubuntu or macOS), run the following command:

```sh
curl -fsSL https://bun.sh/install | bash
```
```sh
curl -fsSL https://bun.sh/install | bash
```

Otherwise, visit the [Bun installation page](https://bun.sh/docs/installation) for installation options.

Expand All @@ -27,7 +27,7 @@ Build the project for production. The result is under `dist/`.

### `bun check`

We use [Biome](https://biomejs.dev/) for **both linting and formatting**. It is an ultra-fast, Rust based linter and formatter.
We use [Biome](https://biomejs.dev/) for **both linting and formatting**. It is an ultra-fast, Rust based linter and formatter.
It also lints JSON.

You can also run `bun fix` to apply any safe fixes automatically.
Expand Down Expand Up @@ -69,8 +69,9 @@ Where the template is:
```

Replacing:
* `<keyword>` with one of `close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`
* `<issue-number>`: the issue number you are fixing

- `<keyword>` with one of `close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`
- `<issue-number>`: the issue number you are fixing

This will let GitHub know the issues are linked, and automatically close them once the PR gets merged. Learn more at [the guide](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword).

Expand Down
28 changes: 14 additions & 14 deletions GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@

To understand the scope of this document, please read:

* [*What is governance?* by Adobe](https://github.com/adobe/open-development-template/blob/master/Governance.md#meritocracy)
* [*Jenkins Governance Document* for an inspiration](https://www.jenkins.io/project/governance/)
- [_What is governance?_ by Adobe](https://github.com/adobe/open-development-template/blob/master/Governance.md#meritocracy)
- [_Jenkins Governance Document_ for an inspiration](https://www.jenkins.io/project/governance/)

## Who we are

We are a group of open-source developers who develop, use, promote the Elysia RealWorld example app, software around it, and other related activities for our mutual benefit.

## Core Philosophy

* **Lower barrier to entry**
- **Lower barrier to entry**

Everyone is welcome to contribute, regardless of their background, experience, or identity. We value all contributions, and we are committed to providing a friendly, safe, and welcoming environment for all. Everyone gets a voice, and everyone is listened to.
Everyone is welcome to contribute, regardless of their background, experience, or identity. We value all contributions, and we are committed to providing a friendly, safe, and welcoming environment for all. Everyone gets a voice, and everyone is listened to.

* **Seeking consensus**
- **Seeking consensus**

We seek consensus among contributors, and we are open to new ideas and approaches. When consensus cannot be reached after giving the stage to all parties, we will use a majority-wins voting process. We will strive to resolve conflicts in a constructive manner.
We seek consensus among contributors, and we are open to new ideas and approaches. When consensus cannot be reached after giving the stage to all parties, we will use a majority-wins voting process. We will strive to resolve conflicts in a constructive manner.

* **Meritocracy**
- **Meritocracy**

We value contributions based on their technical merit, and we welcome new contributors based on their demonstrated ability to contribute. Valuable contributers will be granted access to private channels and will be able to participate in the decision-making process.

* **Transparency**

While the decision-making process is not always public, the results of the decision making process must be public. Decisions will be made after thoughtful consideration of the community's input through [Discord](https://discord.gg/8UcP9QB5AV) and [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions).
We value contributions based on their technical merit, and we welcome new contributors based on their demonstrated ability to contribute. Valuable contributers will be granted access to private channels and will be able to participate in the decision-making process.

* **Automation**
- **Transparency**

We rely on automation to make our processes more efficient and to reduce the burden on contributors. We will automate as much as possible, and we will strive to make our automation tools accessible to all contributors. The goal is to abstract away the mundane tasks and let contributors focus on the fun stuff.
While the decision-making process is not always public, the results of the decision-making process must be public. Decisions will be made after thoughtful consideration of the community's input through [Discord](https://discord.gg/8UcP9QB5AV) and [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions).

- **Automation**

We rely on automation to make our processes more efficient and to reduce the burden on contributors. We will automate as much as possible, and we will strive to make our automation tools accessible to all contributors. The goal is to abstract away the mundane tasks and let contributors focus on the fun stuff.
Loading
Loading