Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move database layer to its own package πŸ“¦ #87

Merged
merged 14 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ Set up your environment variable files by doing the following:
- In `/apps/admin-dashboard`, duplicate the `.env.example` to `.env`.
- In `/apps/api`, duplicate the `.env.example` to `.env`.
- In `/apps/member-profile`, duplicate the `.env.example` to `.env`.
- In `/packages/core`, duplicate the `.env.example` to `.env`.
- In `/packages/core`, duplicate the `.env.test.example` to `.env.test`.
- In `/packages/db`, duplicate the `.env.example` to `.env`.

You'll notice that a lot of environment variables are empty. Most of these empty
variables are tied to the 3rd party integrations we have with platforms such as
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ apps
|--- member-profile
packages
|--- core
|--- db
|--- email-templates
|--- types
|--- ui
Expand All @@ -70,9 +71,10 @@ section).
The `packages` directory contains reusable pieces of code that are used across
our applications.

- `core`: Nearly all of our business logic, including our database layer and
more. Will eventually colocate feature-based UI next to its related business
logic.
- `core`: Nearly all of our core business logic. Will eventually colocate
feature-based UI next to its related business logic.
- `db`: Database layer, which houses all migrations, database scripts (ie:
`seed`) and testing utilities that involve the database.
- `email-templates`: React-based email templates built with
[Resend](https://resend.com).
- `types`: Miscellaneous types shared across applications.
Expand Down
4 changes: 4 additions & 0 deletions apps/admin-dashboard/app/shared/core.server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from '@oyster/core/admin-dashboard.server';

// TODO: Once all database access is moved to the core package, we should
// remove this!
export { db } from '@oyster/db';
1 change: 1 addition & 0 deletions apps/admin-dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@oyster/core": "*",
"@oyster/db": "*",
"@oyster/types": "*",
"@oyster/ui": "*",
"@oyster/utils": "*",
Expand Down
1 change: 1 addition & 0 deletions apps/admin-dashboard/railway.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"watchPatterns": [
"/apps/admin-dashboard/**/*",
"/packages/core/**/*",
"/packages/db/**/*",
"/packages/ui/**/*"
]
},
Expand Down
1 change: 1 addition & 0 deletions apps/api/railway.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"watchPatterns": [
"/apps/api/**/*",
"/packages/core/**/*",
"/packages/db/**/*",
"/packages/email-templates/**/*"
]
},
Expand Down
4 changes: 4 additions & 0 deletions apps/member-profile/app/shared/core.server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from '@oyster/core/member-profile.server';

// TODO: Once all database access is moved to the core package, we should
// remove this!
export { db, type DB } from '@oyster/db';
1 change: 1 addition & 0 deletions apps/member-profile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@oyster/core": "*",
"@oyster/db": "*",
"@oyster/types": "*",
"@oyster/ui": "*",
"@oyster/utils": "*",
Expand Down
1 change: 1 addition & 0 deletions apps/member-profile/railway.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"watchPatterns": [
"/apps/member-profile/**/*",
"/packages/core/**/*",
"/packages/db/**/*",
"/packages/ui/**/*"
]
},
Expand Down
11 changes: 5 additions & 6 deletions docs/how-to-implement-a-database-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ database. Kysely also supports classic "up"/"down" migrations.

## Where Our Migrations Live

All of our database migrations live in our `@oyster/core` package, specifically
[here](../packages/core/src/infrastructure/database/migrations).
All of our database migrations live in our `@oyster/db` package, specifically
[here](../packages/db/src/migrations).

## How to Run Migrations

We have a
[`migrate`](../packages/core/src/infrastructure/database/scripts/migrate.ts)
script, which effectively executes any migrations that haven't been executed
yet. To run this script:
We have a [`migrate`](../packages/db/src/scripts/migrate.ts) script, which
effectively executes any migrations that haven't been executed yet. To run this
script:

```
yarn db:migrate
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
},
"scripts": {
"build": "turbo run build --cache-dir=.turbo",
"db:migrate": "yarn workspace @oyster/core db:migrate",
"db:seed": "yarn workspace @oyster/core db:seed",
"db:setup": "yarn workspace @oyster/core db:setup",
"db:types": "yarn workspace @oyster/core db:types",
"db:migrate": "yarn workspace @oyster/db migrate",
"db:seed": "yarn workspace @oyster/db seed",
"db:setup": "yarn workspace @oyster/db setup",
"db:types": "yarn workspace @oyster/db types",
"dev": "turbo run dev --cache-dir=.turbo",
"dev:apps": "yarn dev --filter='./apps/*'",
"lint": "turbo run lint --cache-dir=.turbo",
"test": "turbo run test --cache-dir=.turbo",
"start": "turbo run start --cache-dir=.turbo",
"test": "turbo run test --cache-dir=.turbo",
"type-check": "turbo run type-check --cache-dir=.turbo"
},
"devDependencies": {
Expand Down
10 changes: 1 addition & 9 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@
},
"scripts": {
"build": "tsup",
"db:migrate": "tsx ./src/infrastructure/database/scripts/migrate.ts && yarn db:types",
"db:migrate:down": "tsx ./src/infrastructure/database/scripts/migrate.ts --down && yarn db:types",
"db:seed": "tsx ./src/infrastructure/database/scripts/seed.ts && yarn db:types",
"db:setup": "tsx ./src/infrastructure/database/scripts/setup.ts",
"db:types": "kysely-codegen --dialect=postgres --camel-case",
"test": "vitest run",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@mailchimp/mailchimp_marketing": "^3.0.78",
"@oyster/db": "*",
"@oyster/email-templates": "*",
"@oyster/types": "*",
"@oyster/utils": "*",
Expand All @@ -41,7 +37,6 @@
"kysely": "^0.26.3",
"nanoid": "^3.0.0",
"nodemailer": "^6.9.13",
"pg": "^8.8.0",
"postmark": "^3.0.15",
"statsig-node": "^5.3.0"
},
Expand All @@ -53,9 +48,6 @@
"@types/jsonwebtoken": "^9.0.2",
"@types/mailchimp__mailchimp_marketing": "^3.0.8",
"@types/nodemailer": "^6.4.14",
"@types/pg": "^8.11.2",
"commander": "^12.0.0",
"kysely-codegen": "^0.10.1",
"nanoid": "^3.0.0",
"react": "^18.2.0",
"tsup": "^6.1.3",
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/admin-dashboard.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export { QueueFromName } from './infrastructure/bull/bull';
export { job } from './infrastructure/bull/use-cases/job';
export { db } from './infrastructure/database';
export { addAdmin } from './modules/admin/use-cases/add-admin';
export { countPendingApplications } from './modules/application/queries/count-pending-applications';
export { getApplication } from './modules/application/queries/get-application';
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/infrastructure/database/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
import { createDatabaseConnection } from './shared/create-database-connection';

export const db = createDatabaseConnection();
export { db } from '@oyster/db';
2 changes: 0 additions & 2 deletions packages/core/src/member-profile.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export type { DB } from 'kysely-codegen/dist/db';
export { job } from './infrastructure/bull/use-cases/job';
export { db } from './infrastructure/database';
export { getActiveStreak } from './modules/active-status/queries/get-active-streak';
export { getActiveStreakLeaderboard } from './modules/active-status/queries/get-active-streak-leaderboard';
export { getActiveStreakLeaderboardPosition } from './modules/active-status/queries/get-active-streak-leaderboard-position';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SelectExpression } from 'kysely';
import type { DB } from 'kysely-codegen/dist/db';

import type { DB } from '@oyster/db';

import { db } from '@/infrastructure/database';

Expand All @@ -8,7 +9,7 @@ type GetApplicationOptions = {
};

export async function getApplication<
Selection extends SelectExpression<DB, 'applications'>
Selection extends SelectExpression<DB, 'applications'>,
>(id: string, selections: Selection[], options: GetApplicationOptions = {}) {
const result = await db
.selectFrom('applications')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SelectExpression } from 'kysely';
import { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

import { db } from '@/infrastructure/database';
import { WorkExperience } from '../employment.types';
Expand All @@ -11,7 +12,7 @@ type GetWorkExperienceOptions = {
};

export async function getWorkExperience<
Selection extends SelectExpression<DB, 'workExperiences'>
Selection extends SelectExpression<DB, 'workExperiences'>,
>(
{ id, studentId }: GetWorkExperienceQuery,
selections: Selection[],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { TEST_COMPANY_1, TEST_COMPANY_4 } from '@oyster/db/test/constants';

import { db } from '@/infrastructure/database';
import {
TEST_COMPANY_1,
TEST_COMPANY_4,
} from '@/infrastructure/database/test/constants';
import * as module from '../queries/get-crunchbase-organization';
import { saveCompanyIfNecessary } from './save-company-if-necessary';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Transaction } from 'kysely';
import { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';
import { id } from '@oyster/utils';

import { db } from '@/infrastructure/database';
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/modules/event/queries/get-event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { SelectExpression } from 'kysely';
import type { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';
import { EventType } from '@oyster/types';

import { db } from '@/infrastructure/database';
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/modules/event/queries/list-events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { SelectExpression } from 'kysely';
import type { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

import { db } from '@/infrastructure/database';

Expand All @@ -10,7 +11,7 @@ type ListEventsQuery = Partial<{
}>;

export async function listEvents<
Selection extends SelectExpression<DB, 'events'>
Selection extends SelectExpression<DB, 'events'>,
>({ limit = 100, page = 1, search }: ListEventsQuery, selections: Selection[]) {
const query = db.selectFrom('events').$if(!!search, (qb) => {
return qb.where('events.name', 'ilike', `%${search}%`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { SelectExpression } from 'kysely';
import type { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

import { db } from '@/infrastructure/database';

export async function getIcebreakerPrompts<
Selection extends SelectExpression<DB, 'icebreakerPrompts'>
Selection extends SelectExpression<DB, 'icebreakerPrompts'>,
>(selections: Selection[]) {
const prompts = await db
.selectFrom('icebreakerPrompts')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { SelectExpression } from 'kysely';
import type { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

import { db } from '@/infrastructure/database';

export async function getIcebreakerResponses<
Selection extends SelectExpression<DB, 'icebreakerResponses'>
Selection extends SelectExpression<DB, 'icebreakerResponses'>,
>(memberId: string, selections: Selection[]) {
const icebreakerResponses = await db
.selectFrom('icebreakerResponses')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Insertable, Transaction } from 'kysely';
import type { DB, IcebreakerResponses } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

export async function upsertIcebreakerResponses(
trx: Transaction<DB>,
memberId: string,
data: Insertable<IcebreakerResponses>[]
data: Insertable<DB['icebreakerResponses']>[]
) {
await trx
.deleteFrom('icebreakerResponses')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Transaction } from 'kysely';
import { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

import { job } from '@/infrastructure/bull/use-cases/job';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Transaction } from 'kysely';
import { DB } from 'kysely-codegen/dist/db';

import { DB } from '@oyster/db';

export async function updateAllowEmailShare(
trx: Transaction<DB>,
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/shared/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const ENV = {
// package and thus in this file after the dotenv has loaded the config.
// Everything else above should be colocated with its respective module.

export const DATABASE_URL = process.env.DATABASE_URL as string;
export const ENVIRONMENT = process.env.ENVIRONMENT as Environment;
export const IS_PRODUCTION = ENVIRONMENT === 'production';
export const IS_TEST = ENVIRONMENT === 'test';
4 changes: 2 additions & 2 deletions packages/core/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export default defineConfig({

// The global setup file is only run once before ALL test suites.
// See: https://vitest.dev/config/#globalsetup
globalSetup: ['./src/infrastructure/database/test/setup.global.ts'],
globalSetup: ['@oyster/db/test/setup.global.ts'],

// The setup files are run before EACH test suite.
// See: https://vitest.dev/config/#setupfiles
setupFiles: ['./src/infrastructure/database/test/setup.ts'],
setupFiles: ['@oyster/db/test/setup.ts'],

// Mocking

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# This is only needed for automatically generating types from the database
# using `kysely-codegen`. See the following:
# https://github.com/RobinBlomberg/kysely-codegen?tab=readme-ov-file#generating-type-definitions
DATABASE_URL=postgresql://colorstack:colorstack@localhost:5432/colorstack
ENVIRONMENT=development
DATABASE_URL=postgresql://colorstack:colorstack@localhost:5432/colorstack
29 changes: 29 additions & 0 deletions packages/db/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@oyster/db",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./test/*": "./src/test/*",
"./test/constants": "./src/test/constants.ts"
},
"scripts": {
"migrate": "tsx ./src/scripts/migrate.ts && yarn types",
"migrate:down": "tsx ./src/scripts/migrate.ts --down && yarn types",
"seed": "tsx ./src/scripts/seed.ts && yarn types",
"setup": "tsx ./src/scripts/setup.ts",
"type-check": "tsc --noEmit",
"types": "kysely-codegen --dialect=postgres --camel-case"
},
"dependencies": {
"kysely": "^0.26.3",
"pg": "^8.8.0"
},
"devDependencies": {
"@oyster/tsconfig": "*",
"@types/pg": "^8.11.2",
"commander": "^12.0.0",
"kysely-codegen": "^0.10.1"
}
}
2 changes: 2 additions & 0 deletions packages/db/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { DB } from 'kysely-codegen/dist/db';
export { db } from './shared/db';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { program } from 'commander';

import { migrate } from '../shared/migrate';
import { migrate } from '../use-cases/migrate';

const DOWN_FLAG = '--down';

Expand Down
Loading