
Product
Unify Your Security Stack with Socket Basics
A single platform for static analysis, secrets detection, container scanning, and CVE checks—built on trusted open source tools, ready to run out of the box.
@cosmicmind/streamjs
Advanced tools
A domain-driven design framework for scalable systems.
Inspired by domain-driven design (DDD), DomainJS is a domain-driven design framework for scalable systems.
In Domain-driven design (DDD) an entity is a representation of an object within a given domain, for example: a book, product order, and user would be entities within a domain that handles purchase orders for an e-commerce website that sold books. Let's take a look at an entity type definition for a user within our domain.
// User.ts
import {
Entity,
} from '@cosmicmind/domainjs'
export type User = Entity & {
id: string
name: string
age: number
}
The above example is a user entity type definition. It represents an object, in this case a user within our domain. The user is defined by the id, name, and age attribute values. When working with entities, DomainJS will immediately help within the following areas of concern:
Let's take a look at the following code example to understand entity validation in DomainJS.
import {
User,
} from './User'
function someFunction(user: User): void {
// ... do something
}
const user: User = {
id: '123',
name: 'Sarah',
age: 29,
}
// ...
someFunction(user)
In the above code, we can see that the user passed to someFunction actually doesn't provide any guarantees of its validity. In order to guarantee validity within
someFunction, validation logic would need to be executed within the function itself. An issue arises when we need to constantly validate our entities, causing validation
logic to exist in multiple places within our codebase. DomainJS defines the validation logic within the entity itself and calls the appropriate validators when creating and
updating entities, for example:
// User.ts
// ...
export const makeUser = defineEntity<User>({
attributes: {
id: {
validator(value): boolean | never {
// id validation logic
// return true | false
// or throw an error
},
},
name: {
validator(value): boolean | never {
// name validation logic
// return true | false
// or throw an error
},
},
age: {
validator(value): boolean | never {
// age validation logic
// return true | false
// or throw an error
},
},
},
})
import {
makeUser,
} from './User'
function someFunction(user: User): void {
// ... do something
}
const user = makeUser({
id: '123',
name: 'Sarah',
age: 29,
})
console.log(user.id) // "123"
console.log(user.name) // "Sarah"
console.log(user.age) // 29
someFunction(user)
By using the constructor function returned by defineEntity<User>(...), each user entity is guaranteed to be valid.
It is impossible for the user entity to be created and reach the code at line someFunction(user) if it is invalid.
DomainJS organizes lifecycle hooks within the entity definition itself, like so:
export const makeUser = defineEntity<User>({
created(user) {
// ... do something
},
trace(user) {
// ... do something
},
attributes: {
// ...
age: {
validator(value): boolean | never {
// ... do something
},
udpated(newValue, oldValue, user): void {
// ... do something
},
},
// ...
},
})
The above example shows the various lifecycle hooks available for entities. Let's take a look at each one of these hooks to understand when they are executed.
The created lifecycle hook is executed only once when an instance is initially created.
The updated lifecycle hook is executed after an attribute has been updated.
The trace lifecycle hook is executed after the created and updated lifecycle hooks.
A Value Object (VO) in Domain-driven design encapsulates a single value and its validity. Further to ensuring its validity, a VO provides specific functionality that is relevant to the value itself, for example:
// Email.ts
import {
Value,
defineValue,
} from '@cosmicmind/domainjs'
export class Email extends Value<string> {
get domainAddress(): string {
return this.value.split('@')[1]
}
}
export const makeEmail = defineValue(Email, {
created(email): void {
// ... do something
},
trace(email) {
// ... do something
},
validator(value): boolean | never {
// email validation logic
// return true | false
// or throw an error
},
})
import {
makeEmail,
} from './Email'
const email = makeEmail('me@domain.com')
console.log(email.value) // "me@domain.com"
console.log(email.domainAddress) // "domain.com"
... more to come ...
// User.ts
import {
Entity,
defineEntity,
} from '@cosmicmind/domainjs'
import {
Email,
} from './Email'
export type User = Entity & {
id: string
name: string
age: number
email: Email
}
export const makeUser = defineEntity<User>({
attributes: {
id: {
validator(value): boolean | never {
// id validation logic
// return true | false
// or throw an error
},
},
name: {
validator(value): boolean | never {
// name validation logic
// return true | false
// or throw an error
},
},
age: {
validator(value): boolean | never {
// age validation logic
// return true | false
// or throw an error
},
},
},
})
import {
makeUser,
} from './User'
import {
makeEmail,
} from './Email'
const user = makeUser({
id: '123',
name: 'Daniel',
age: 29,
email: makeEmail('me@domain.com'),
})
console.log(user.id) // "123"
console.log(user.name) // "Daniel"
console.log(user.age) // 29
console.log(user.email.value) // "me@domain.com"
console.log(user.email.domainAddress) // "domain.com"
Value Objects are great for parameter passing and letting the function know that it is using a valid value. For example:
import {
Email,
} from './Email'
function someFunction(email: Email): void {
if ('domain.com' === email.domainAddress) {
// ... do something
}
}
... more to come ...
... more to come ...
// UserAggregate.ts
import {
Aggregate,
defineAggregate,
} from '@cosmicmind/domainjs'
import {
User,
} from './User'
import {
Email,
} from './Email'
export class UserAggregate extends Aggregate<User> {
get id(): string {
return this.root.id
}
get email(): Email {
return this.root.email
}
registerAccount(): void {
// ... do something
}
}
export const makeUserAggregate = defineAggregate(UserAggregate, {
created(user) {
// ... do something
},
trace(user) {
// ... do something
},
attributes: {
// ...
age: {
validator(value): boolean | never {
// ... do something
},
udpated(newValue, oldValue, user): void {
// ... do something
},
},
// ...
},
})
import {
makeUserAggregate
} from './UserAggregate'
import {
makeEmail,
} from './Email'
const user = makeUserAggregate({
id: '123',
name: 'Daniel',
age: 29,
email: makeEmail('me@domain.com'),
})
console.log(user.id) // "123"
console.log(user.name) // error cannot access (not exposed in UserAggragte)
console.log(user.age) // error cannot access (not exposed in UserAggragte)
console.log(user.email.value) // "me@domain.com"
console.log(user.email.domainAddress) // "domain.com"
user.registerAccount() // ... account registration process
... more to come ...
... more to come ...
// RegisterAccountEvent.ts
import {
Event,
defineEvent,
} from '@cosmicmind/domainjs'
import {
User,
} from './User'
export type RegisterAccountEvent = Event & {
id: string
user: User
}
export const createRegisterAccountEvent = defineEvent<RegisterAccountEvent>({
attributes: {
id: {
validator(value): boolean | never {
// id validation logic
// return true | false
// or throw an error
},
},
},
})
Now that we have our RegisterAccountEvent, let's add it to the UserAggregate example.
// UserAggregate.ts
import {
Aggregate,
defineAggregate,
EventTopics,
} from '@cosmicmind/domainjs'
// ...
import {
RegisterAccountEvent,
createRegisterAccountEvent,
} from './RegisterAccountEvent'
export type UserAggregateEventTopics = EventTopics & {
'register-account': RegisterAccountEvent
}
export class UserAggregate extends Aggregate<User, UserAggregateEventTopics> {
// ...
registerAccount(): void {
// ... do something
this.publishSync('register-account', createRegisterAccountEvent({
id: '123',
user: this.root,
}))
}
}
// ...
import {
makeUserAggregate
} from './UserAggregate'
import {
makeEmail,
} from './Email'
const user = makeUserAggregate({
id: '123',
name: 'Daniel',
age: 29,
email: makeEmail('me@domain.com'),
})
user.subscribe('register-account', (event: RegisterAccountEvent) => {
// ... do something
})
console.log(user.id) // "123"
console.log(user.name) // error cannot access (not exposed in UserAggragte)
console.log(user.age) // error cannot access (not exposed in UserAggragte)
console.log(user.email.value) // "me@domain.com"
console.log(user.email.domainAddress) // "domain.com"
user.registerAccount() // ... account registration process and event is published
Additional documentation and examples will follow shortly. If you have any examples or use cases that you are interested in exploring, please create a discussion.
Thank you!
FAQs
A domain-driven design framework for scalable systems.
We found that @cosmicmind/streamjs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
A single platform for static analysis, secrets detection, container scanning, and CVE checks—built on trusted open source tools, ready to run out of the box.

Product
Socket is launching experimental protection for the Hugging Face ecosystem, scanning for malware and malicious payload injections inside model files to prevent silent AI supply chain attacks.

Research
/Security News
The Socket Threat Research Team uncovered a coordinated campaign that floods the Chrome Web Store with 131 rebranded clones of a WhatsApp Web automation extension to spam Brazilian users.