| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 1 | 1 | 0% |
| Commands | 0 | 0 | 0 | — |
| Section tags | 0 | 1 | 1 | 0% |
What each file covers
Sections
0 shared · 1 only in A · 1 only in B- − ClickHouse Conventions
- + DAL Repository Rules
Commands
neither file has anySection tags
0 shared · 1 only in A · 1 only in B- − code-style
- + do-not
Line diff
novuhq/novu · .cursor/rules/clickhouse.mdc
@@ −1 @@
1---
2description: Rules for working with ClickHouse analytics and trace logging
3globs:
4 - "**/analytic-logs/**"
5 - "**/clickhouse-migrations/**"
6alwaysApply: false
7---
8
9### ClickHouse Conventions
10
11**Service layer**
12- Use `ClickHouseService` for single queries/inserts and `ClickHouseBatchService` for high-throughput writes.
13- Both are registered as custom providers (`clickHouseService`, `clickHouseBatchService`) in each app's `shared.module.ts`.
14- Never instantiate ClickHouse clients directly — always inject the providers.
15
16**Repositories**
17- All ClickHouse repositories extend `LogRepository` in `libs/application-generic/src/services/analytic-logs/`.
18- Each repository has a corresponding schema file defining the table columns and types.
19- Always include `_environmentId` and `_organizationId` in queries for tenant isolation (same pattern as MongoDB DAL).
20
21**Migrations**
22- ClickHouse migrations are numbered `.sql` files in `apps/api/migrations/clickhouse-migrations/`.
23- Run locally with `cd apps/api && pnpm run clickhouse:migrate:local`.
24- New migrations must be additive — never alter or drop columns that existing queries depend on. Use temp tables and exchange patterns for schema refactors (see migration 4/5 for the established pattern).
25
26**Feature flags**
27- Gate new ClickHouse-dependent behavior behind a feature flag (see `packages/shared/src/types/feature-flags.ts` for existing flags like `IS_CLICKHOUSE_BATCHING_ENABLED`).
28
novuhq/novu · .cursor/rules/dal-repository.mdc
@@ +1 @@
1---
2description: Rules for working with DAL repositories in the Novu monorepo
3globs: libs/dal/**/*.ts, **/repositories/**/*.ts
4alwaysApply: false
5---
6
7### DAL Repository Rules
8
9#### Choosing a base class
10
11- **New repositories must extend `BaseRepositoryV2`** — it enforces required field selection and provides auto-inferred return types (`Pick<Entity, Keys>`).
12- **Existing repositories stay on `BaseRepository`** — `BaseRepository` is deprecated but must not be changed; all 32 existing repos continue to extend it.
13- Do NOT extend `BaseRepository` for any new repository going forward.
14
15#### BaseRepositoryV2 — required `select`
16
17- Every read method (`find`, `findOne`, `findById`, `findBatch`, `findWithCursorBasedPagination`) requires an explicit `select` argument — there is no default `SELECT *`.
18- Use array syntax as the default: `find(query, ['_id', 'name', 'status'])`. Array syntax returns exactly the listed fields — `_id` is excluded unless explicitly included.
19- Use object syntax when you need MongoDB-style projections where `_id` is included by default: `findOne(query, { name: 1, email: 1 })`, or explicitly excluded: `findOne(query, { _id: 0, name: 1 })`.
20- Exclusion projections for non-`_id` fields (e.g. `{ name: 0 }`) are intentionally unsupported — they are a compile error.
21- Return types are automatically inferred as `Pick<Entity, Keys>` — do not manually annotate the return type.
22- Use `select: '*'` to retrieve all fields with a fully-typed `Entity` return (instead of a `Pick`). All five read methods support this overload: `find(query, '*')`, `findOne(query, '*')`, `findById(id, '*')`, `findBatch(query, '*')`, and `findWithCursorBasedPagination({ select: '*', ... })`.
23- Omitting `select` entirely is still a compile error — `'*'` is the explicit opt-in for SELECT *.
24
25#### Enforcement (applies to both V1 and V2)
26
27- **Never use `_model` or `MongooseModel` directly** in repository methods. Always use the inherited methods (`update`, `find`, `findOne`, `delete`, `create`, `bulkWrite`, etc.) which enforce `_environmentId` / `_organizationId` via the `EnforceEnvOrOrgIds` type.
28- All query methods must include `_environmentId` or `_organizationId` in their filter to satisfy the enforcement type constraint.
29- When adding new repository methods that need `$push`, `$pull`, or other update operators, pass the `environmentId` as a parameter and use `this.update()` with the enforcement fields.
30- For bulk operations, use `this.bulkWrite()` instead of `this._model.updateMany()`.
31- **Transactions**: start via `repository.withTransaction(async (session) => { ... })` and pass `session` to every repo call inside it (e.g. `repo.findOne(query, select, { session })`). Run all operations sequentially — parallel execution (`Promise.all`, etc.) inside a transaction is undefined behaviour in Mongoose.
32
@@ −1 +1 @@
11 ---
2−description: Rules for working with ClickHouse analytics and trace logging
3−globs:
4− - "**/analytic-logs/**"
5− - "**/clickhouse-migrations/**"
2+description: Rules for working with DAL repositories in the Novu monorepo
3+globs: libs/dal/**/*.ts, **/repositories/**/*.ts
64 alwaysApply: false
75 ---
86
9−### ClickHouse Conventions
7+### DAL Repository Rules
108
11−**Service layer**
12−- Use `ClickHouseService` for single queries/inserts and `ClickHouseBatchService` for high-throughput writes.
13−- Both are registered as custom providers (`clickHouseService`, `clickHouseBatchService`) in each app's `shared.module.ts`.
14−- Never instantiate ClickHouse clients directly — always inject the providers.
9+#### Choosing a base class
1510
16−**Repositories**
17−- All ClickHouse repositories extend `LogRepository` in `libs/application-generic/src/services/analytic-logs/`.
18−- Each repository has a corresponding schema file defining the table columns and types.
19−- Always include `_environmentId` and `_organizationId` in queries for tenant isolation (same pattern as MongoDB DAL).
11+- **New repositories must extend `BaseRepositoryV2`** — it enforces required field selection and provides auto-inferred return types (`Pick<Entity, Keys>`).
12+- **Existing repositories stay on `BaseRepository`** — `BaseRepository` is deprecated but must not be changed; all 32 existing repos continue to extend it.
13+- Do NOT extend `BaseRepository` for any new repository going forward.
2014
21−**Migrations**
22−- ClickHouse migrations are numbered `.sql` files in `apps/api/migrations/clickhouse-migrations/`.
23−- Run locally with `cd apps/api && pnpm run clickhouse:migrate:local`.
24−- New migrations must be additive — never alter or drop columns that existing queries depend on. Use temp tables and exchange patterns for schema refactors (see migration 4/5 for the established pattern).
15+#### BaseRepositoryV2 — required `select`
2516
26−**Feature flags**
27−- Gate new ClickHouse-dependent behavior behind a feature flag (see `packages/shared/src/types/feature-flags.ts` for existing flags like `IS_CLICKHOUSE_BATCHING_ENABLED`).
17+- Every read method (`find`, `findOne`, `findById`, `findBatch`, `findWithCursorBasedPagination`) requires an explicit `select` argument — there is no default `SELECT *`.
18+- Use array syntax as the default: `find(query, ['_id', 'name', 'status'])`. Array syntax returns exactly the listed fields — `_id` is excluded unless explicitly included.
19+- Use object syntax when you need MongoDB-style projections where `_id` is included by default: `findOne(query, { name: 1, email: 1 })`, or explicitly excluded: `findOne(query, { _id: 0, name: 1 })`.
20+- Exclusion projections for non-`_id` fields (e.g. `{ name: 0 }`) are intentionally unsupported — they are a compile error.
21+- Return types are automatically inferred as `Pick<Entity, Keys>` — do not manually annotate the return type.
22+- Use `select: '*'` to retrieve all fields with a fully-typed `Entity` return (instead of a `Pick`). All five read methods support this overload: `find(query, '*')`, `findOne(query, '*')`, `findById(id, '*')`, `findBatch(query, '*')`, and `findWithCursorBasedPagination({ select: '*', ... })`.
23+- Omitting `select` entirely is still a compile error — `'*'` is the explicit opt-in for SELECT *.
24+
25+#### Enforcement (applies to both V1 and V2)
26+
27+- **Never use `_model` or `MongooseModel` directly** in repository methods. Always use the inherited methods (`update`, `find`, `findOne`, `delete`, `create`, `bulkWrite`, etc.) which enforce `_environmentId` / `_organizationId` via the `EnforceEnvOrOrgIds` type.
28+- All query methods must include `_environmentId` or `_organizationId` in their filter to satisfy the enforcement type constraint.
29+- When adding new repository methods that need `$push`, `$pull`, or other update operators, pass the `environmentId` as a parameter and use `this.update()` with the enforcement fields.
30+- For bulk operations, use `this.bulkWrite()` instead of `this._model.updateMany()`.
31+- **Transactions**: start via `repository.withTransaction(async (session) => { ... })` and pass `session` to every repo call inside it (e.g. `repo.findOne(query, select, { session })`). Run all operations sequentially — parallel execution (`Promise.all`, etc.) inside a transaction is undefined behaviour in Mongoose.
2832
