declapract-typescript-ehmpathy 0.45.4 → 0.45.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/practices/cicd-package/best-practice/.github/workflows/provision.yml +1 -1
  2. package/dist/practices/cicd-service/best-practice/.github/workflows/provision.yml +1 -1
  3. package/dist/practices/directory-structure-src/bad-practices/data-dir/.declapract.readme.md +10 -0
  4. package/dist/practices/directory-structure-src/bad-practices/data-dir/src/data/<star><star>/<star>.ts.declapract.ts +19 -0
  5. package/dist/practices/directory-structure-src/bad-practices/domain-dir/.declapract.readme.md +8 -0
  6. package/dist/practices/directory-structure-src/bad-practices/domain-dir/src/domain/<star><star>/<star>.ts.declapract.ts +17 -0
  7. package/dist/practices/directory-structure-src/bad-practices/logic-dir/.declapract.readme.md +8 -0
  8. package/dist/practices/directory-structure-src/bad-practices/logic-dir/src/logic/<star><star>/<star>.ts.declapract.ts +17 -0
  9. package/dist/practices/directory-structure-src/bad-practices/model-dir/.declapract.readme.md +3 -2
  10. package/dist/practices/directory-structure-src/bad-practices/model-dir/src/<star><star>/<star>.ts.declapract.ts +6 -4
  11. package/dist/practices/directory-structure-src/bad-practices/model-dir/src/model/<star><star>/<star>.ts.declapract.ts +3 -3
  12. package/dist/practices/directory-structure-src/bad-practices/old-import-paths/.declapract.readme.md +11 -0
  13. package/dist/practices/directory-structure-src/bad-practices/old-import-paths/src/<star><star>/<star>.ts.declapract.ts +41 -0
  14. package/dist/practices/directory-structure-src/best-practice/.declapract.readme.md +48 -7
  15. package/dist/practices/directory-structure-src/best-practice/src/{data/clients → access/daos}/<star><star>/<star>.ts.declapract.ts +2 -1
  16. package/dist/practices/directory-structure-src/best-practice/src/access/sdks/<star><star>/<star>.ts.declapract.ts +4 -0
  17. package/dist/practices/directory-structure-src/best-practice/src/access/svcs/<star><star>/<star>.ts.declapract.ts +4 -0
  18. package/dist/practices/directory-structure-src/best-practice/src/contract/<star><star>/<star>.ts.declapract.ts +1 -1
  19. package/dist/practices/directory-structure-src/best-practice/src/domain.objects/<star><star>/<star>.ts.declapract.ts +4 -0
  20. package/dist/practices/directory-structure-src/best-practice/src/{domain/index.ts.declapract.ts → domain.operations/<star><star>/<star>.ts.declapract.ts} +1 -0
  21. package/dist/practices/directory-structure-src/best-practice/src/{data/dao → infra}/<star><star>/<star>.ts.declapract.ts +2 -1
  22. package/dist/practices/persist-with-dynamodb/best-practice/codegen.dynamodb.dao.ts +2 -2
  23. package/dist/practices/persist-with-rds/best-practice/codegen.sql.dao.yml +2 -2
  24. package/dist/practices/persist-with-rds/best-practice/codegen.sql.types.yml +6 -6
  25. package/dist/practices/provision-github/bad-practices/old-declastruct-resources-path/provision/github/declastruct.resources.ts.declapract.ts +7 -0
  26. package/package.json +1 -1
  27. package/dist/practices/directory-structure-src/best-practice/src/domain/constants.ts.declapract.ts +0 -15
  28. package/dist/practices/directory-structure-src/best-practice/src/domain/objects/index.ts.declapract.ts +0 -4
  29. package/dist/practices/directory-structure-src/best-practice/src/logic/<star><star>/<star>.ts.declapract.ts +0 -4
  30. /package/dist/practices/provision-github/best-practice/provision/{github/declastruct.resources.ts → github.repo/resources.ts} +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  github:
19
19
  uses: ./.github/workflows/.declastruct.yml
20
20
  with:
21
- wish-path: provision/github/declastruct.resources.ts
21
+ wish-path: provision/github.repo/resources.ts
22
22
  github-environment: prod
23
23
  creds-github-app-owner: ehmpathy
24
24
  creds-github-app-id: ${{ vars.DECLASTRUCT_GITHUB_CONFORMER_APP_ID }}
@@ -43,7 +43,7 @@ jobs:
43
43
  github:
44
44
  uses: ./.github/workflows/.declastruct.yml
45
45
  with:
46
- wish-path: provision/github/declastruct.resources.ts
46
+ wish-path: provision/github.repo/resources.ts
47
47
  github-environment: prod
48
48
  creds-github-app-owner: ehmpathy
49
49
  creds-github-app-id: ${{ vars.DECLASTRUCT_GITHUB_CONFORMER_APP_ID }}
@@ -0,0 +1,10 @@
1
+ the `src/data/` directory pattern is deprecated
2
+
3
+ use `src/access/` instead, which contains:
4
+ - `daos/` - data access objects for persistence
5
+ - `sdks/` - third party remote service contracts
6
+ - `svcs/` - first party remote service contracts
7
+
8
+ this aligns with the directional-dependencies architecture where:
9
+ - `access/` may depend on `domain.objects/` and `domain.operations/`
10
+ - `access/` must remain free of domain knowledge and business rules
@@ -0,0 +1,19 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ // if files exist in src/data/, this is a bad practice
4
+ export const check = FileCheckType.EXISTS;
5
+
6
+ export const fix: FileFixFunction = (contents, context) => {
7
+ // move src/data/dao/* to src/access/daos/*
8
+ // move src/data/clients/* to src/access/sdks/*
9
+ // move other src/data/* to src/access/*
10
+ const newPath = context.relativeFilePath
11
+ .replace('src/data/dao/', 'src/access/daos/')
12
+ .replace('src/data/clients/', 'src/access/sdks/')
13
+ .replace('src/data/', 'src/access/');
14
+
15
+ return {
16
+ contents,
17
+ relativeFilePath: newPath,
18
+ };
19
+ };
@@ -0,0 +1,8 @@
1
+ the `src/domain/` directory pattern is deprecated
2
+
3
+ use `src/domain.objects/` instead for domain object declarations
4
+
5
+ this aligns with the directional-dependencies architecture where:
6
+ - `domain.objects/` contains canonical domain declarations
7
+ - `domain.objects/` must not depend on anything outside its own layer
8
+ - separates domain objects from domain operations for clearer boundaries
@@ -0,0 +1,17 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ // if files exist in src/domain/, this is a bad practice
4
+ export const check = FileCheckType.EXISTS;
5
+
6
+ export const fix: FileFixFunction = (contents, context) => {
7
+ // move src/domain/objects/* to src/domain.objects/*
8
+ // move src/domain/* to src/domain.objects/*
9
+ const newPath = context.relativeFilePath
10
+ .replace('src/domain/objects/', 'src/domain.objects/')
11
+ .replace('src/domain/', 'src/domain.objects/');
12
+
13
+ return {
14
+ contents,
15
+ relativeFilePath: newPath,
16
+ };
17
+ };
@@ -0,0 +1,8 @@
1
+ the `src/logic/` directory pattern is deprecated
2
+
3
+ use `src/domain.operations/` instead for domain behavior and business rules
4
+
5
+ this aligns with the directional-dependencies architecture where:
6
+ - `domain.operations/` contains domain behavior and business rules
7
+ - `domain.operations/` may depend on `domain.objects/` or `infra/`
8
+ - `domain.operations/` must not reference infrastructure concerns directly
@@ -0,0 +1,17 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ // if files exist in src/logic/, this is a bad practice
4
+ export const check = FileCheckType.EXISTS;
5
+
6
+ export const fix: FileFixFunction = (contents, context) => {
7
+ // move src/logic/* to src/domain.operations/*
8
+ const newPath = context.relativeFilePath.replace(
9
+ 'src/logic/',
10
+ 'src/domain.operations/',
11
+ );
12
+
13
+ return {
14
+ contents,
15
+ relativeFilePath: newPath,
16
+ };
17
+ };
@@ -1,2 +1,3 @@
1
- we use `domain` directory instead of `model` directory
2
- - model directory was what we originally went with, but realized `domain` was more explicit and better represented what we were going after
1
+ we use `domain.objects` directory instead of `model` directory
2
+ - model directory was what we originally went with, but realized `domain.objects` was more explicit and better represented what we were going after
3
+ - files are migrated directly from `src/model/` to `src/domain.objects/`
@@ -20,13 +20,14 @@ export const check: FileCheckFunction = (contents, context) => {
20
20
  const badImportIdentifier = defineModelImportFromRelativePathName({
21
21
  relativeFilePath: context.relativeFilePath,
22
22
  });
23
- if (contents?.includes(badImportIdentifier)) return; // dont fail since it matches bad practice
23
+ if (contents?.includes(badImportIdentifier)) return; // relative import to model dir
24
+ if (contents?.includes(`@src/model`)) return; // absolute @src/model import
24
25
  if (contents?.includes(`/domain/domainObjects`)) return; // matches bad practice if it has import to `.../domainObjects/...` as well
25
26
  throw new Error('does not match bad practice'); // TODO: make this less weird... its weird that we throw an error when its not bad practice
26
27
  };
27
28
 
28
29
  /**
29
- * replace `.../model` imports with `.../domain` imporst
30
+ * replace `.../model` imports with `.../domain.objects` imports
30
31
  */
31
32
  export const fix: FileFixFunction = (contents, context) => {
32
33
  const badImportIdentifier = defineModelImportFromRelativePathName({
@@ -37,8 +38,9 @@ export const fix: FileFixFunction = (contents, context) => {
37
38
  contents: contents
38
39
  .replace(
39
40
  badImportIdentifier,
40
- badImportIdentifier.replace('/model', '/domain'), // should be referencing the domain dir instead
41
+ badImportIdentifier.replace('/model', '/domain.objects'), // should be referencing the domain.objects dir instead
41
42
  )
42
- .replace('/domainObjects', '/objects'), // and should still replace this type of import, too
43
+ .replace(/@src\/model\b/g, '@src/domain.objects') // fix absolute @src/model imports
44
+ .replace(/\/domainObjects/g, ''), // remove the domainObjects subdir as domain.objects is flat now
43
45
  };
44
46
  };
@@ -7,10 +7,10 @@ export const fix: FileFixFunction = (contents, context) => {
7
7
  contents:
8
8
  contents?.replace(
9
9
  "export * from './domainObjects';", // if it has this, we want to replace it
10
- "export * from './objects';",
10
+ "export * from './';",
11
11
  ) ?? null,
12
12
  relativeFilePath: context.relativeFilePath
13
- .replace('src/model/', 'src/domain/')
14
- .replace('domainObjects/', 'objects/'),
13
+ .replace('src/model/domainObjects/', 'src/domain.objects/')
14
+ .replace('src/model/', 'src/domain.objects/'),
15
15
  };
16
16
  };
@@ -0,0 +1,11 @@
1
+ imports referencing old directory structure paths should be updated
2
+
3
+ old → new:
4
+ - `src/data/dao` → `src/access/daos`
5
+ - `src/data/clients` → `src/access/sdks`
6
+ - `src/data/` → `src/access/`
7
+ - `src/domain/objects` → `src/domain.objects`
8
+ - `src/domain/` → `src/domain.objects/`
9
+ - `src/logic/` → `src/domain.operations/`
10
+
11
+ this bad practice checks all source files for imports using the old paths and fixes them.
@@ -0,0 +1,41 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ // patterns to detect old import paths
4
+ const OLD_PATH_PATTERNS = [
5
+ /from\s+['"][^'"]*\/data\/dao/,
6
+ /from\s+['"][^'"]*\/data\/clients/,
7
+ /from\s+['"][^'"]*\/data\//,
8
+ /from\s+['"][^'"]*\/domain\/objects/,
9
+ /from\s+['"][^'"]*\/domain\//,
10
+ /from\s+['"][^'"]*\/logic\//,
11
+ ];
12
+
13
+ export const check: FileCheckFunction = (contents) => {
14
+ if (!contents) throw new Error('no contents');
15
+
16
+ // check if any old import path pattern matches
17
+ const hasOldImport = OLD_PATH_PATTERNS.some((pattern) =>
18
+ pattern.test(contents),
19
+ );
20
+
21
+ if (hasOldImport) return; // matches bad practice
22
+ throw new Error('does not match bad practice');
23
+ };
24
+
25
+ export const fix: FileFixFunction = (contents) => {
26
+ if (!contents) return { contents };
27
+
28
+ // fix imports in order of specificity (most specific first)
29
+ const fixed = contents
30
+ // data layer fixes
31
+ .replace(/(['"])([^'"]*?)\/data\/dao\b/g, '$1$2/access/daos')
32
+ .replace(/(['"])([^'"]*?)\/data\/clients\b/g, '$1$2/access/sdks')
33
+ .replace(/(['"])([^'"]*?)\/data\//g, '$1$2/access/')
34
+ // domain layer fixes
35
+ .replace(/(['"])([^'"]*?)\/domain\/objects\b/g, '$1$2/domain.objects')
36
+ .replace(/(['"])([^'"]*?)\/domain\//g, '$1$2/domain.objects/')
37
+ // logic layer fixes
38
+ .replace(/(['"])([^'"]*?)\/logic\//g, '$1$2/domain.operations/');
39
+
40
+ return { contents: fixed };
41
+ };
@@ -1,4 +1,34 @@
1
- layers to a project:
1
+ layers in a project following directional-dependencies architecture:
2
+
3
+ ```
4
+ src/
5
+ contract/ # topmost — public interfaces, local commands
6
+ api/ # public invocable api endpoints, deployed and exposed by the project
7
+ cmd/ # private internal use entrypoints, supported by the project
8
+ sdk/ # public software development kit exports, supported by the project
9
+ cli/ # public command line interface contracts, supported by the project
10
+
11
+ access/ # infrastructure layer (daos, sdks, svcs)
12
+ daos/ # private persistence logic — may reference domain objects
13
+ sdks/ # remote third party contracts, from any alt org
14
+ svcs/ # remote first party contracts, from our own org
15
+
16
+ domain.objects/ # canonical domain declarations
17
+ domain.operations/ # domain behavior + business rules
18
+
19
+ infra/ # infrastructure specific adapters
20
+ ```
21
+
22
+ dependency rules:
23
+ - each layer may depend **only on the layers below it**
24
+ - `contract/` may depend on `domain.objects/` and `domain.operations/`
25
+ - `access/` may depend on `domain.objects/` and `domain.operations/`
26
+ - `domain.operations/` may depend on `domain.objects/` or `infra/`
27
+ - `domain.objects/` must not depend on anything outside its own layer
28
+ - `infra/` must not depend on anything outside its own layer
29
+
30
+ layer responsibilities:
31
+
2
32
  - contract layer
3
33
  - summary: responsible for strictly exposing the logic/domain defined in the service
4
34
  - responsibilities:
@@ -10,23 +40,34 @@ layers to a project:
10
40
  - separates how users interact with our business logic _from_ our business logic
11
41
  - supporting-references:
12
42
  - https://docs.aws.amazon.com/whitepapers/latest/serverless-architectures-lambda/business-logic-outside-the-handler.html
13
- - domain layer
43
+
44
+ - domain.objects layer
14
45
  - summary: defines the domain which this service masters
15
46
  - responsibilities:
16
47
  - specify domain objects
17
48
  - specify constants
18
49
  - purpose:
19
50
  - a canonical definition of the domain + ubiquitous language owned by the service
20
- - logic layer
51
+
52
+ - domain.operations layer
21
53
  - summary: defines the business logic that this service manages
22
54
  - responsibilities:
23
55
  - define the business logic of the domain in an organized and maintainable way
24
56
  - purpose:
25
57
  - to explicitly define the business logic as close to human language as possible
26
- - data layer:
58
+
59
+ - access layer
27
60
  - summary: defines how to persist data / leverage third party clients
28
61
  - responsibilities:
29
- - daos
30
- - clients
62
+ - daos (data access objects)
63
+ - sdks (third party remote service contracts)
64
+ - svcs (first party remote service contracts)
31
65
  - purpose:
32
- - separates how to interact with data stores (our own persistance stores + third party client + first party clients) from our business logic
66
+ - separates how to interact with data stores and remote services from our business logic
67
+
68
+ - infra layer
69
+ - summary: defines infrastructure-specific adapters
70
+ - responsibilities:
71
+ - infrastructure adapters that bridge domain logic to specific infrastructure
72
+ - purpose:
73
+ - keeps infrastructure concerns isolated from domain logic
@@ -1,3 +1,4 @@
1
1
  import { FileCheckType } from 'declapract';
2
2
 
3
- export const check = { type: FileCheckType.EXISTS, optional: true }; // not all services have clients
3
+ // daos are optional - not all services have persistence
4
+ export const check = { type: FileCheckType.EXISTS, optional: true };
@@ -0,0 +1,4 @@
1
+ import { FileCheckType } from 'declapract';
2
+
3
+ // sdks (third party remote service contracts) are optional - not all services use external apis
4
+ export const check = { type: FileCheckType.EXISTS, optional: true };
@@ -0,0 +1,4 @@
1
+ import { FileCheckType } from 'declapract';
2
+
3
+ // svcs (first party remote service contracts) are optional - not all services call other internal services
4
+ export const check = { type: FileCheckType.EXISTS, optional: true };
@@ -1,4 +1,4 @@
1
1
  import { FileCheckType } from 'declapract';
2
2
 
3
- // files matching this format should exist - there should be some contract in this service
3
+ // contract layer is required - there should be some contract in this service (api, sdk, cli, cmd)
4
4
  export const check = FileCheckType.EXISTS;
@@ -0,0 +1,4 @@
1
+ import { FileCheckType } from 'declapract';
2
+
3
+ // domain objects are required - what is the purpose of a service without domain objects?
4
+ export const check = FileCheckType.EXISTS;
@@ -1,3 +1,4 @@
1
1
  import { FileCheckType } from 'declapract';
2
2
 
3
+ // domain operations are required - there should be business logic in the service
3
4
  export const check = FileCheckType.EXISTS;
@@ -1,3 +1,4 @@
1
1
  import { FileCheckType } from 'declapract';
2
2
 
3
- export const check = { type: FileCheckType.EXISTS, optional: true }; // not all services have a dao
3
+ // infra layer is optional - not all services need infrastructure-specific adapters
4
+ export const check = { type: FileCheckType.EXISTS, optional: true };
@@ -5,12 +5,12 @@ import {
5
5
  } from 'dynamodb-dao-generator';
6
6
 
7
7
  export const introspect: DeclaredDomainObjectIntrospectionPaths = [
8
- './src/domain/index.ts',
8
+ './src/domain.objects/index.ts',
9
9
  ];
10
10
 
11
11
  export const directories: DeclaredOutputDirectories = {
12
12
  terraform: `provision/aws/product`,
13
- dao: `src/data/dao`,
13
+ dao: `src/access/daos`,
14
14
  };
15
15
 
16
16
  export const specifications: DeclaredDaoSpecification[] = [];
@@ -2,7 +2,7 @@ language: postgres
2
2
  dialect: 10.7
3
3
  generates:
4
4
  daos:
5
- to: src/data/dao
5
+ to: src/access/daos
6
6
  using:
7
7
  log: src/utils/logger#log
8
8
  DatabaseConnection: src/utils/database/getDatabaseConnection#DatabaseConnection
@@ -15,4 +15,4 @@ generates:
15
15
  for:
16
16
  objects:
17
17
  search:
18
- - 'src/domain/objects/*.ts'
18
+ - 'src/domain.objects/*.ts'
@@ -5,12 +5,12 @@ resources: # where to find your tables, functions, views, procedures
5
5
  - 'provision/schema/sql/views/**/*.sql'
6
6
  - 'provision/schema/sql/functions/**/*.sql'
7
7
  queries: # where to find your queries (each file must `export const query = `...`);
8
- - 'src/data/dao/**/*.ts'
9
- - '!src/data/dao/**/index.ts'
10
- - '!src/data/dao/**/castFromDatabaseObject.ts'
11
- - '!src/data/dao/**/findByRef.ts'
8
+ - 'src/access/daos/**/*.ts'
9
+ - '!src/access/daos/**/index.ts'
10
+ - '!src/access/daos/**/castFromDatabaseObject.ts'
11
+ - '!src/access/daos/**/findByRef.ts'
12
12
  - '!src/**/*.test.ts'
13
13
  - '!src/**/*.test.integration.ts'
14
14
  generates: # where to output the generated code
15
- types: src/data/dao/.generated/types.ts
16
- queryFunctions: src/data/dao/.generated/queryFunctions.ts
15
+ types: src/access/daos/.generated/types.ts
16
+ queryFunctions: src/access/daos/.generated/queryFunctions.ts
@@ -0,0 +1,7 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.EXISTS;
4
+
5
+ export const fix: FileFixFunction = () => {
6
+ return { contents: null }; // delete old path, replaced by provision/github.repo/resources.ts
7
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "declapract-typescript-ehmpathy",
3
3
  "author": "ehmpathy",
4
4
  "description": "declapract best practices declarations for typescript",
5
- "version": "0.45.4",
5
+ "version": "0.45.6",
6
6
  "license": "MIT",
7
7
  "main": "src/index.js",
8
8
  "repository": "ehmpathy/declapract-typescript-ehmpathy",
@@ -1,15 +0,0 @@
1
- import { FileCheckType, type FileFixFunction } from 'declapract';
2
-
3
- export const check = FileCheckType.EXISTS;
4
-
5
- export const fix: FileFixFunction = (contents) => {
6
- if (contents) return {}; // if contents exist, do nothing
7
- return {
8
- contents: `${`
9
- /**
10
- * constants shared across the whole service but not tied to a specific domain object go in this file
11
- */
12
- `.trim()}
13
- `,
14
- };
15
- };
@@ -1,4 +0,0 @@
1
- import { FileCheckType } from 'declapract';
2
-
3
- // project should have domain objects... otherwise, what is the purpose of this thing? (how can we have a service that does not operate on a domain?)
4
- export const check = FileCheckType.EXISTS;
@@ -1,4 +0,0 @@
1
- import { FileCheckType } from 'declapract';
2
-
3
- // files matching this format should exist - there should be business logic in the service
4
- export const check = FileCheckType.EXISTS;