diff --git a/readme.md b/readme.md index 062bb33..d611694 100644 --- a/readme.md +++ b/readme.md @@ -58,7 +58,7 @@ Consequently, by utilizing the schema generator: - you can comfortably modify any generated schema to support edge cases or differences in opinion not already supported by the library, as you fully manage the sql that was generated ### Inspiration -- [Domain Driven Design](https://dddcommunity.org/learning-ddd/what_is_ddd/): domain driven design provides tools to define the Domain Model in a ubiquitous language (Entities, Value Objects, etc), which increase increases the maintainability and utility of the infrastructure you spend time building. +- [Domain Driven Design](https://dddcommunity.org/learning-ddd/what_is_ddd/): domain driven design provides tools to define the Domain Model in a ubiquitous language (Entities, Literals, etc), which increase increases the maintainability and utility of the infrastructure you spend time building. - [Temporal Database Design](https://dba.stackexchange.com/a/114738/75296): temporal database design provides a way to optimally implement the "insert only" data lifecycle pattern, retaining the changes an entity goes through over time while also maintaining foreign keys, uniqueness constraints, and normalization. # Usage @@ -72,9 +72,9 @@ Consequently, by utilizing the schema generator: ```ts // for example: in /schema/entities.ts - import { Entity, prop, ValueObject } from '../module'; + import { Entity, prop, Literal } from '../module'; - const photo = new ValueObject({ + const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(), diff --git a/src/contract/__test_assets__/domain.ts b/src/contract/__test_assets__/domain.ts index 5cce9b2..57533dd 100644 --- a/src/contract/__test_assets__/domain.ts +++ b/src/contract/__test_assets__/domain.ts @@ -1,7 +1,7 @@ import { Event } from '../../domain'; -import { Entity, prop, ValueObject } from '../module'; +import { Entity, prop, Literal } from '../module'; -const photo = new ValueObject({ +const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(255), diff --git a/src/contract/module.ts b/src/contract/module.ts index 992a184..fd14dbe 100644 --- a/src/contract/module.ts +++ b/src/contract/module.ts @@ -4,6 +4,6 @@ export { Entity, Event, Property, - ValueObject, + Literal, } from '../domain'; export { prop } from '../logic/define'; diff --git a/src/domain/objects/Event.ts b/src/domain/objects/Event.ts index 5dd4ca9..c7c7eca 100644 --- a/src/domain/objects/Event.ts +++ b/src/domain/objects/Event.ts @@ -8,7 +8,7 @@ import { Property } from './Property'; interface EventConstructorProps { name: string; properties: { - [index: string]: Omit; // by definition, value objects are not updatable + [index: string]: Omit; // by definition, literals are not updatable }; unique: string[]; } diff --git a/src/domain/objects/ValueObject.test.ts b/src/domain/objects/Literal.test.ts similarity index 77% rename from src/domain/objects/ValueObject.test.ts rename to src/domain/objects/Literal.test.ts index 179d151..a19e073 100644 --- a/src/domain/objects/ValueObject.test.ts +++ b/src/domain/objects/Literal.test.ts @@ -1,15 +1,15 @@ import { DataTypeName } from '../constants'; import { DataType } from './DataType'; +import { Literal } from './Literal'; import { Property } from './Property'; -import { ValueObject } from './ValueObject'; -describe('ValueObject', () => { +describe('Literal', () => { const type = new DataType({ name: DataTypeName.VARCHAR, precision: 255, }); it('should be possible to initialize with valid data', () => { - const address = new ValueObject({ + const address = new Literal({ name: 'address', properties: { name: new Property({ @@ -19,12 +19,12 @@ describe('ValueObject', () => { }), }, }); - expect(address).toBeInstanceOf(ValueObject); + expect(address).toBeInstanceOf(Literal); }); it('should throw an error if the property is "updatable"', () => { try { // tslint:disable-next-line: no-unused-expression - new ValueObject({ + new Literal({ name: 'address', properties: { name: new Property({ @@ -37,7 +37,7 @@ describe('ValueObject', () => { throw new Error('should not reach here'); } catch (error) { expect(error.message).toEqual( - 'value objects can not have updateable properties, by definition', + 'literals can not have updateable properties, by definition', ); } }); diff --git a/src/domain/objects/ValueObject.ts b/src/domain/objects/Literal.ts similarity index 71% rename from src/domain/objects/ValueObject.ts rename to src/domain/objects/Literal.ts index 3639e6e..9dc5f0c 100644 --- a/src/domain/objects/ValueObject.ts +++ b/src/domain/objects/Literal.ts @@ -2,18 +2,18 @@ import { Entity } from './Entity'; import { Property } from './Property'; /* - turns out: Domain Value Objects, from a persistance perspective, are just like Domain Entities - except with more constraints: + turns out: Domain Literals, from a persistance perspective, are just like Domain Entities - except with more constraints: - i.e., no updatable values - i.e., it is unique on all properties */ -interface ValueObjectConstructorProps { +interface LiteralConstructorProps { name: string; properties: { - [index: string]: Omit; // by definition, value objects are not updatable + [index: string]: Omit; // by definition, literals are not updatable }; } -export class ValueObject extends Entity { - constructor(props: ValueObjectConstructorProps) { +export class Literal extends Entity { + constructor(props: LiteralConstructorProps) { // 1. check that all the properties are immutable; this shouldn't be allowed w/ type checking but lets run time validate it too const updateableProperties = Object.keys(props.properties).filter( (propertyName) => { @@ -24,7 +24,7 @@ export class ValueObject extends Entity { ); if (updateableProperties.length) throw new Error( - 'value objects can not have updateable properties, by definition', + 'literals can not have updateable properties, by definition', ); // 2. define the unique condition automatically, as all properties diff --git a/src/domain/objects/index.ts b/src/domain/objects/index.ts index fff80e9..a3eea6a 100644 --- a/src/domain/objects/index.ts +++ b/src/domain/objects/index.ts @@ -1,5 +1,5 @@ export { DataType } from './DataType'; export { Property } from './Property'; export { Entity, Properties } from './Entity'; -export { ValueObject } from './ValueObject'; +export { Literal } from './Literal'; export { Event } from './Event'; diff --git a/src/logic/compose/generateSchema/generateAndRecordEntitySchema/generateAndRecordEntitySchema.integration.test.ts b/src/logic/compose/generateSchema/generateAndRecordEntitySchema/generateAndRecordEntitySchema.integration.test.ts index 635be2d..1df1484 100644 --- a/src/logic/compose/generateSchema/generateAndRecordEntitySchema/generateAndRecordEntitySchema.integration.test.ts +++ b/src/logic/compose/generateSchema/generateAndRecordEntitySchema/generateAndRecordEntitySchema.integration.test.ts @@ -1,12 +1,12 @@ -import { Entity, ValueObject } from '../../../../domain'; +import { Entity, Literal } from '../../../../domain'; import * as prop from '../../../define/defineProperty'; import { generateAndRecordEntitySchema } from './generateAndRecordEntitySchema'; import { readFile } from './utils/fileIO'; describe('generateAndRecordEntitySchema', () => { const targetDirPath = `${__dirname}/__test_assets__/generated`; - it('should record all resources for a value object (i.e., non updatable entity)', async () => { - const address = new ValueObject({ + it('should record all resources for a literal (i.e., non updatable entity)', async () => { + const address = new Literal({ name: 'address', properties: { street: prop.VARCHAR(255), @@ -105,13 +105,13 @@ describe('generateAndRecordEntitySchema', () => { expect(viewCurrentSql).toContain('CREATE OR REPLACE VIEW'); }); it('should record all resources for an updatable entity with array properties', async () => { - const language = new ValueObject({ + const language = new Literal({ name: 'language', properties: { name: prop.VARCHAR(255), }, }); - const producer = new ValueObject({ + const producer = new Literal({ name: 'producer', properties: { name: prop.VARCHAR(255), diff --git a/src/logic/compose/generateSchema/normalizeDeclarationContents/normalizeDeclarationContents.test.ts b/src/logic/compose/generateSchema/normalizeDeclarationContents/normalizeDeclarationContents.test.ts index 180f139..389c7be 100644 --- a/src/logic/compose/generateSchema/normalizeDeclarationContents/normalizeDeclarationContents.test.ts +++ b/src/logic/compose/generateSchema/normalizeDeclarationContents/normalizeDeclarationContents.test.ts @@ -1,4 +1,4 @@ -import { Entity, ValueObject } from '../../../../domain'; +import { Entity, Literal } from '../../../../domain'; import { prop } from '../../../define'; import { normalizeDeclarationContents } from './normalizeDeclarationContents'; import { throwErrorIfAnyReservedPropertyNamesAreUsed } from './throwErrorIfAnyReservedPropertyNamesAreUsed'; @@ -34,7 +34,7 @@ describe('normalizeDeclarationContents', () => { ); } }); - it('throw an error if entities are not all of class Entity or ValueObject', () => { + it('throw an error if entities are not all of class Entity or Literal', () => { const contents = { entities: ['not an Entity'] }; try { normalizeDeclarationContents({ contents }); @@ -101,12 +101,12 @@ describe('normalizeDeclarationContents', () => { entity: exampleEntity, }); }); - it('should return the entities and value objects found in the contents', () => { - const plant = new ValueObject({ + it('should return the entities and literals found in the contents', () => { + const plant = new Literal({ name: 'plant', properties: { genus: prop.VARCHAR(255) }, }); - const vase = new ValueObject({ + const vase = new Literal({ name: 'vase', properties: { plants: prop.ARRAY_OF(prop.REFERENCES(plant)) }, }); @@ -131,12 +131,12 @@ describe('normalizeDeclarationContents', () => { // check that we return everything as expected expect(entities).toEqual([plant, vase, customer, order]); }); - it('should return the entities and value objects found in the contents - when specified with the generateSqlSchemasFor syntax', () => { - const plant = new ValueObject({ + it('should return the entities and literals found in the contents - when specified with the generateSqlSchemasFor syntax', () => { + const plant = new Literal({ name: 'plant', properties: { genus: prop.VARCHAR(255) }, }); - const vase = new ValueObject({ + const vase = new Literal({ name: 'vase', properties: { plants: prop.ARRAY_OF(prop.REFERENCES(plant)) }, }); diff --git a/src/logic/compose/generateSchema/normalizeDeclarationContents/throwErrorIfNamingConventionsNotFollowed/throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix.test.ts b/src/logic/compose/generateSchema/normalizeDeclarationContents/throwErrorIfNamingConventionsNotFollowed/throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix.test.ts index be9faae..a6fc87b 100644 --- a/src/logic/compose/generateSchema/normalizeDeclarationContents/throwErrorIfNamingConventionsNotFollowed/throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix.test.ts +++ b/src/logic/compose/generateSchema/normalizeDeclarationContents/throwErrorIfNamingConventionsNotFollowed/throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix.test.ts @@ -1,10 +1,10 @@ -import { Entity, ValueObject } from '../../../../../domain'; +import { Entity, Literal } from '../../../../../domain'; import { prop } from '../../../../define'; import { throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix } from './throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix'; describe('throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix', () => { it('should throw an error if property references another but does not have correct name', () => { - const photo = new ValueObject({ + const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(255) }, }); @@ -25,7 +25,7 @@ describe('throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix', () => { } }); it('should throw an error if an array property references another but does not have correct name', () => { - const photo = new ValueObject({ + const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(255) }, }); @@ -49,7 +49,7 @@ describe('throwErrorIfAnyReferencePropertyDoesNotHaveExplicitSuffix', () => { } }); it('should not throw an error for an entity with correctly named reference properties', () => { - const photo = new ValueObject({ + const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(255) }, }); diff --git a/src/logic/define/defineProperty.test.ts b/src/logic/define/defineProperty.test.ts index 824e29d..8d8cedb 100644 --- a/src/logic/define/defineProperty.test.ts +++ b/src/logic/define/defineProperty.test.ts @@ -1,4 +1,4 @@ -import { DataTypeName, Entity, Property, ValueObject } from '../../domain'; +import { DataTypeName, Entity, Property, Literal } from '../../domain'; import * as prop from './defineProperty'; describe('generateProperty', () => { @@ -28,7 +28,7 @@ describe('generateProperty', () => { }); }); it('should throw an error if entity REFERENCES_VERSION of a non-updatable entity', () => { - const apple = new ValueObject({ + const apple = new Literal({ name: 'apple', properties: { name: prop.VARCHAR(255), // e.g., Granny Smith diff --git a/src/logic/generate/entityFunctions/generateEntityUpsert/generateEntityUpsert.integration.test.ts b/src/logic/generate/entityFunctions/generateEntityUpsert/generateEntityUpsert.integration.test.ts index d552ff8..115f5df 100644 --- a/src/logic/generate/entityFunctions/generateEntityUpsert/generateEntityUpsert.integration.test.ts +++ b/src/logic/generate/entityFunctions/generateEntityUpsert/generateEntityUpsert.integration.test.ts @@ -4,7 +4,7 @@ import { pg as prepare } from 'yesql'; import { normalizeCreateFunctionDdl } from '../../../../__nonpublished_modules__/postgres-show-create-ddl/showCreateFunction/normalizeCreateFunctionDdl'; import { getShowCreateFunction } from '../../../../__test_utils__/getShowCreateFunction'; import { uuid as uuidV4 } from '../../../../deps'; -import { Entity, ValueObject } from '../../../../domain'; +import { Entity, Literal } from '../../../../domain'; import * as prop from '../../../define/defineProperty'; import { createTablesForEntity, @@ -444,13 +444,13 @@ describe('generateEntityUpsert', () => { }); describe('entity with array properties', () => { - const language = new ValueObject({ + const language = new Literal({ name: 'language', properties: { name: prop.VARCHAR(), }, }); - const producer = new ValueObject({ + const producer = new Literal({ name: 'producer', properties: { name: prop.VARCHAR(), @@ -1138,10 +1138,10 @@ describe('generateEntityUpsert', () => { }, unique: ['make', 'model', 'year'], }); - const crashReport = new ValueObject({ + const crashReport = new Literal({ name: 'crash_report', properties: { - location_id: prop.BIGINT(), // this should reference a real "location" value object, but for this example lets just leave it as a bigint + location_id: prop.BIGINT(), // this should reference a real "location" literal, but for this example lets just leave it as a bigint vehicle_version_id: prop.REFERENCES_VERSION(vehicle), // reference the specific vehicle version, as maybe a software glitch is causing the crashes }, }); diff --git a/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToFunctionInputDefinition.test.ts b/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToFunctionInputDefinition.test.ts index dc17ccd..a599108 100644 --- a/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToFunctionInputDefinition.test.ts +++ b/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToFunctionInputDefinition.test.ts @@ -1,4 +1,4 @@ -import { ValueObject } from '../../../../../domain'; +import { Literal } from '../../../../../domain'; import { prop } from '../../../../define'; import { castPropertyToFunctionInputDefinition } from './castPropertyToFunctionInputDefinition'; @@ -11,7 +11,7 @@ describe('castPropertyToFunctionInputDefinition', () => { expect(definition).toMatchSnapshot(); }); it('should define the definition accurately for an array property', () => { - const tag = new ValueObject({ + const tag = new Literal({ name: 'tag', properties: { name: prop.VARCHAR(255) }, }); diff --git a/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToWhereClauseConditional.test.ts b/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToWhereClauseConditional.test.ts index 693ecc6..3d33bb5 100644 --- a/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToWhereClauseConditional.test.ts +++ b/src/logic/generate/entityFunctions/generateEntityUpsert/utils/castPropertyToWhereClauseConditional.test.ts @@ -1,9 +1,9 @@ -import { Entity, ValueObject } from '../../../../../domain'; +import { Entity, Literal } from '../../../../../domain'; import { prop } from '../../../../define'; import { castPropertyToWhereClauseConditional } from './castPropertyToWhereClauseConditional'; describe('castPropertyToWhereClauseConditional', () => { - const user = new ValueObject({ + const user = new Literal({ name: 'user', properties: { name: prop.VARCHAR(255) }, }); diff --git a/src/logic/generate/entityTables/generateEntityTables.integration.test.ts b/src/logic/generate/entityTables/generateEntityTables.integration.test.ts index d23ecb1..733292f 100644 --- a/src/logic/generate/entityTables/generateEntityTables.integration.test.ts +++ b/src/logic/generate/entityTables/generateEntityTables.integration.test.ts @@ -3,7 +3,7 @@ import { getDatabaseConnection, } from '../../../__test_utils__/databaseConnection'; import { getShowCreateTable } from '../../../__test_utils__/getShowCreateTable'; -import { Entity, ValueObject } from '../../../domain'; +import { Entity, Literal } from '../../../domain'; import * as prop from '../../define/defineProperty'; import { createTablesForEntity, dropTablesForEntity } from '../__test_utils__'; import { generateEntityTables } from './generateEntityTables'; @@ -170,13 +170,13 @@ describe('generateEntityTables', () => { expect(createVersionTable).toEqual(tables.version!.sql); // should be the exact string }); it('generates tables for an entity with array properties (both updatable and static) and unique on one array, w/ the same syntax as show create', async () => { - const photo = new ValueObject({ + const photo = new Literal({ name: 'photo', properties: { url: prop.VARCHAR(255), }, }); - const host = new ValueObject({ + const host = new Literal({ name: 'host', properties: { name: prop.VARCHAR(255), diff --git a/src/logic/generate/entityTables/generateEntityTables.test.ts b/src/logic/generate/entityTables/generateEntityTables.test.ts index b2ec60e..2b6fadb 100644 --- a/src/logic/generate/entityTables/generateEntityTables.test.ts +++ b/src/logic/generate/entityTables/generateEntityTables.test.ts @@ -1,4 +1,4 @@ -import { Entity, ValueObject } from '../../../domain'; +import { Entity, Literal } from '../../../domain'; import * as prop from '../../define/defineProperty'; import { generateEntityTables } from './generateEntityTables'; import { generateMappingTablesForArrayProperties } from './generateMappingTablesForArrayProperties'; @@ -88,13 +88,13 @@ describe('generateEntityTables', () => { ); }); it('should generateMappingTablesForArrayProperties for the array properties only', () => { - const language = new ValueObject({ + const language = new Literal({ name: 'language', properties: { name: prop.VARCHAR(255), }, }); - const producer = new ValueObject({ + const producer = new Literal({ name: 'producer', properties: { name: prop.VARCHAR(255), diff --git a/src/logic/generate/entityViews/generateEntityCurrentView.integration.test.ts b/src/logic/generate/entityViews/generateEntityCurrentView.integration.test.ts index b5def34..fd686e7 100644 --- a/src/logic/generate/entityViews/generateEntityCurrentView.integration.test.ts +++ b/src/logic/generate/entityViews/generateEntityCurrentView.integration.test.ts @@ -5,7 +5,7 @@ import { getDatabaseConnection, } from '../../../__test_utils__/databaseConnection'; import { uuid } from '../../../deps'; -import { Entity, ValueObject } from '../../../domain'; +import { Entity, Literal } from '../../../domain'; import * as prop from '../../define/defineProperty'; import { createTablesForEntity, dropTablesForEntity } from '../__test_utils__'; import { dropAndCreateUpsertFunctionForEntity } from '../__test_utils__/dropAndCreateUpsertForEntity'; @@ -47,14 +47,14 @@ describe('generateEntityViewCurrent', () => { }); }); describe('with array properties', () => { - const lock = new ValueObject({ + const lock = new Literal({ name: 'door_lock', properties: { manufacturer: prop.VARCHAR(255), manufacturerId: prop.VARCHAR(255), }, }); - const door = new ValueObject({ + const door = new Literal({ name: 'door', properties: { color: prop.ENUM(['red', 'green', 'blue']), @@ -256,7 +256,7 @@ describe('generateEntityViewCurrent', () => { }); }); describe('with array properties', () => { - const wheel = new ValueObject({ + const wheel = new Literal({ name: 'wheel', properties: { name: prop.VARCHAR(255),