@swoft/ddd
Version:
Domain-Driven Design (DDD) strategic and tactical design tools for domain modeling and bounded context management.
1,387 lines (1,371 loc) • 514 kB
JavaScript
'use strict';
var crypto$1 = require('crypto');
var uuid = require('uuid');
var persistence = require('@swoft/persistence');
var inversify = require('inversify');
var core = require('@swoft/core');
var zod = require('zod');
var actualFS = require('fs');
var path = require('path');
var ai = require('ai');
var fs3 = require('fs/promises');
var url = require('url');
var events = require('events');
var Stream = require('stream');
var string_decoder = require('string_decoder');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var actualFS__namespace = /*#__PURE__*/_interopNamespace(actualFS);
var path__namespace = /*#__PURE__*/_interopNamespace(path);
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
var Stream__default = /*#__PURE__*/_interopDefault(Stream);
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (decorator(result)) || result;
return result;
};
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
// src/infrastructure/di/DDD_SYMBOLS.ts
exports.DDD_SYMBOLS = void 0;
var init_DDD_SYMBOLS = __esm({
"src/infrastructure/di/DDD_SYMBOLS.ts"() {
exports.DDD_SYMBOLS = {
// Application Services
DomainApplicationService: Symbol.for("DomainApplicationService"),
BoundedContextApplicationService: Symbol.for("BoundedContextApplicationService"),
AggregateApplicationService: Symbol.for("AggregateApplicationService"),
ProjectApplicationService: Symbol.for("ProjectApplicationService"),
PackageMappingApplicationService: Symbol.for("PackageMappingApplicationService"),
// Query Services
DomainCQRSQueryService: Symbol.for("DomainCQRSQueryService"),
BoundedContextQueryService: Symbol.for("BoundedContextQueryService"),
AggregateQueryService: Symbol.for("AggregateQueryService"),
ProjectQueryService: Symbol.for("ProjectQueryService"),
// Command Services
DomainCommandService: Symbol.for("DomainCommandService"),
BoundedContextCommandService: Symbol.for("BoundedContextCommandService"),
AggregateCommandService: Symbol.for("AggregateCommandService"),
ProjectCommandService: Symbol.for("ProjectCommandService"),
// Repositories
IDomainRepository: Symbol.for("IDomainRepository"),
IBoundedContextRepository: Symbol.for("IBoundedContextRepository"),
IAggregateRepository: Symbol.for("IAggregateRepository"),
IProjectRepository: Symbol.for("IProjectRepository"),
IPackageMappingRepository: Symbol.for("IPackageMappingRepository"),
// Command Handlers
CreateDomainHandler: Symbol.for("CreateDomainHandler"),
UpdateDomainHandler: Symbol.for("UpdateDomainHandler"),
GetDomainHandler: Symbol.for("GetDomainHandler"),
ListDomainsHandler: Symbol.for("ListDomainsHandler"),
CreateBoundedContextHandler: Symbol.for("CreateBoundedContextHandler"),
UpdateBoundedContextHandler: Symbol.for("UpdateBoundedContextHandler"),
GetBoundedContextHandler: Symbol.for("GetBoundedContextHandler"),
ListBoundedContextsHandler: Symbol.for("ListBoundedContextsHandler"),
CreateAggregateHandler: Symbol.for("CreateAggregateHandler"),
UpdateAggregateHandler: Symbol.for("UpdateAggregateHandler"),
GetAggregateHandler: Symbol.for("GetAggregateHandler"),
CreateProjectHandler: Symbol.for("CreateProjectHandler"),
UpdateProjectHandler: Symbol.for("UpdateProjectHandler"),
GetProjectHandler: Symbol.for("GetProjectHandler"),
ListProjectsHandler: Symbol.for("ListProjectsHandler"),
// Infrastructure
IEventBus: Symbol.for("IEventBus"),
ICommandBus: Symbol.for("ICommandBus"),
IQueryBus: Symbol.for("IQueryBus"),
// Domain Services
IDomainAnalysisService: Symbol.for("IDomainAnalysisService"),
IBoundedContextAnalysisService: Symbol.for("IBoundedContextAnalysisService"),
// AI Integration
AIAnalysisService: Symbol.for("AIAnalysisService"),
PersonLookupPort: Symbol.for("PersonLookupPort"),
// Value Objects and Factories
DomainIdFactory: Symbol.for("DomainIdFactory"),
BoundedContextIdFactory: Symbol.for("BoundedContextIdFactory"),
AggregateIdFactory: Symbol.for("AggregateIdFactory"),
// Controllers
DomainController: Symbol.for("DomainController"),
BoundedContextController: Symbol.for("BoundedContextController"),
AggregateController: Symbol.for("AggregateController"),
ProjectController: Symbol.for("ProjectController"),
PackageMappingController: Symbol.for("PackageMappingController"),
// Database Port
DatabasePort: Symbol.for("DatabasePort")
};
}
});
exports.Domain = void 0;
var init_Domain = __esm({
"src/bounded-contexts/strategic-design/domain/entities/Domain.ts"() {
exports.Domain = class _Domain {
id;
createdAt;
_updatedAt;
_name;
_description;
_vision;
_ubiquitousLanguage;
constructor(properties, id) {
this.id = id || crypto$1.randomUUID();
this.createdAt = /* @__PURE__ */ new Date();
this._updatedAt = /* @__PURE__ */ new Date();
this._name = properties.name;
this._description = properties.description;
this._vision = properties.vision;
this._ubiquitousLanguage = properties.ubiquitousLanguage;
this.validateInvariants();
}
static create(properties) {
return new _Domain(properties);
}
static reconstitute(properties, id, createdAt, updatedAt) {
const domain = new _Domain(properties, id);
domain.createdAt = createdAt;
domain._updatedAt = updatedAt;
return domain;
}
get name() {
return this._name;
}
get description() {
return this._description;
}
get vision() {
return this._vision;
}
get ubiquitousLanguage() {
return { ...this._ubiquitousLanguage };
}
get updatedAt() {
return this._updatedAt;
}
updateName(name) {
if (!name || name.trim().length === 0) {
throw new Error("Domain name cannot be empty");
}
this._name = name.trim();
this.touch();
}
updateDescription(description) {
this._description = description?.trim() || void 0;
this.touch();
}
updateVision(vision) {
this._vision = vision?.trim() || void 0;
this.touch();
}
addUbiquitousLanguageTerm(term, definition) {
if (!term || !definition) {
throw new Error("Term and definition are required");
}
this._ubiquitousLanguage[term.trim()] = definition.trim();
this.touch();
}
removeUbiquitousLanguageTerm(term) {
delete this._ubiquitousLanguage[term];
this.touch();
}
touch() {
this._updatedAt = /* @__PURE__ */ new Date();
}
validateInvariants() {
if (!this._name || this._name.trim().length === 0) {
throw new Error("Domain name is required");
}
if (this._name.length > 100) {
throw new Error("Domain name must be 100 characters or less");
}
if (this._description && this._description.length > 1e3) {
throw new Error("Domain description must be 1000 characters or less");
}
if (this._vision && this._vision.length > 2e3) {
throw new Error("Domain vision must be 2000 characters or less");
}
}
toJSON() {
return {
id: this.id,
name: this._name,
description: this._description,
vision: this._vision,
ubiquitousLanguage: this._ubiquitousLanguage,
createdAt: this.createdAt,
updatedAt: this._updatedAt
};
}
};
}
});
exports.BoundedContext = void 0;
var init_BoundedContext = __esm({
"src/bounded-contexts/strategic-design/domain/entities/BoundedContext.ts"() {
exports.BoundedContext = class _BoundedContext {
id;
createdAt;
_updatedAt;
_domainId;
_name;
_description;
_type;
_ubiquitousLanguage;
_domainServices;
_aggregates;
_valueObjects;
_entities;
constructor(properties, id) {
this.id = id || crypto$1.randomUUID();
this.createdAt = /* @__PURE__ */ new Date();
this._updatedAt = /* @__PURE__ */ new Date();
this._domainId = properties.domainId;
this._name = properties.name;
this._description = properties.description;
this._type = properties.type;
this._ubiquitousLanguage = properties.ubiquitousLanguage;
this._domainServices = properties.domainServices;
this._aggregates = properties.aggregates;
this._valueObjects = properties.valueObjects;
this._entities = properties.entities;
this.validateInvariants();
}
static create(properties) {
return new _BoundedContext(properties);
}
static reconstitute(properties, id, createdAt, updatedAt) {
const context = new _BoundedContext(properties, id);
context.createdAt = createdAt;
context._updatedAt = updatedAt;
return context;
}
get domainId() {
return this._domainId;
}
get name() {
return this._name;
}
get description() {
return this._description;
}
get type() {
return this._type;
}
get ubiquitousLanguage() {
return { ...this._ubiquitousLanguage };
}
get domainServices() {
return [...this._domainServices];
}
get aggregates() {
return [...this._aggregates];
}
get valueObjects() {
return [...this._valueObjects];
}
get entities() {
return [...this._entities];
}
get updatedAt() {
return this._updatedAt;
}
updateName(name) {
if (!name || name.trim().length === 0) {
throw new Error("Bounded context name cannot be empty");
}
this._name = name.trim();
this.touch();
}
updateDescription(description) {
this._description = description?.trim() || void 0;
this.touch();
}
updateType(type) {
this._type = type;
this.touch();
}
addUbiquitousLanguageTerm(term, definition) {
if (!term || !definition) {
throw new Error("Term and definition are required");
}
this._ubiquitousLanguage[term.trim()] = definition.trim();
this.touch();
}
removeUbiquitousLanguageTerm(term) {
delete this._ubiquitousLanguage[term];
this.touch();
}
addDomainService(service) {
if (!service.name) {
throw new Error("Service name is required");
}
if (!this._domainServices.find((s) => s.id === service.id)) {
this._domainServices.push(service);
this.touch();
}
}
removeDomainService(serviceId) {
const index = this._domainServices.findIndex((s) => s.id === serviceId);
if (index > -1) {
this._domainServices.splice(index, 1);
this.touch();
}
}
addAggregate(aggregate) {
if (!aggregate.name) {
throw new Error("Aggregate name is required");
}
if (!this._aggregates.find((a) => a.id === aggregate.id)) {
this._aggregates.push(aggregate);
this.touch();
}
}
removeAggregate(aggregateId) {
const index = this._aggregates.findIndex((a) => a.id === aggregateId);
if (index > -1) {
this._aggregates.splice(index, 1);
this.touch();
}
}
addValueObject(valueObject) {
if (!valueObject.name) {
throw new Error("Value object name is required");
}
if (!this._valueObjects.find((v) => v.id === valueObject.id)) {
this._valueObjects.push(valueObject);
this.touch();
}
}
removeValueObject(valueObjectId) {
const index = this._valueObjects.findIndex((v) => v.id === valueObjectId);
if (index > -1) {
this._valueObjects.splice(index, 1);
this.touch();
}
}
addEntity(entity) {
if (!entity.name) {
throw new Error("Entity name is required");
}
if (!this._entities.find((e) => e.id === entity.id)) {
this._entities.push(entity);
this.touch();
}
}
removeEntity(entityId) {
const index = this._entities.findIndex((e) => e.id === entityId);
if (index > -1) {
this._entities.splice(index, 1);
this.touch();
}
}
touch() {
this._updatedAt = /* @__PURE__ */ new Date();
}
validateInvariants() {
if (!this._domainId) {
throw new Error("Domain ID is required");
}
if (!this._name || this._name.trim().length === 0) {
throw new Error("Bounded context name is required");
}
if (this._name.length > 100) {
throw new Error("Bounded context name must be 100 characters or less");
}
if (this._description && this._description.length > 1e3) {
throw new Error("Bounded context description must be 1000 characters or less");
}
}
toJSON() {
return {
id: this.id,
domainId: this._domainId,
name: this._name,
description: this._description,
type: this._type,
ubiquitousLanguage: this._ubiquitousLanguage,
domainServices: this._domainServices,
aggregates: this._aggregates,
valueObjects: this._valueObjects,
entities: this._entities,
createdAt: this.createdAt,
updatedAt: this._updatedAt
};
}
};
}
});
exports.PackageMapping = void 0; exports.PackageType = void 0; exports.MappingStatus = void 0;
var init_PackageMapping = __esm({
"src/bounded-contexts/meta-model-design/domain/entities/PackageMapping.ts"() {
exports.PackageMapping = class _PackageMapping {
constructor(_id, _packageName, _packageType, _domainId, _classification, _status, _createdAt, _updatedAt, _lastReviewedBy = null, _notes = null) {
this._id = _id;
this._packageName = _packageName;
this._packageType = _packageType;
this._domainId = _domainId;
this._classification = _classification;
this._status = _status;
this._createdAt = _createdAt;
this._updatedAt = _updatedAt;
this._lastReviewedBy = _lastReviewedBy;
this._notes = _notes;
}
// Factory method for creating new package mappings
static create(packageName, packageType, domainId, classification) {
const now = /* @__PURE__ */ new Date();
const defaultClassification = classification || {
confidence: 0.5,
reasoning: "Manual classification",
suggestedDomain: domainId || null,
automaticallyClassified: false
};
return new _PackageMapping(
uuid.v4(),
packageName,
packageType,
domainId || null,
defaultClassification,
domainId ? "aligned" /* ALIGNED */ : "orphaned" /* ORPHANED */,
now,
now
);
}
// Factory method for reconstituting from persistence
static reconstitute(id, packageName, packageType, domainId, classification, status, createdAt, updatedAt, lastReviewedBy, notes) {
return new _PackageMapping(
id,
packageName,
packageType,
domainId,
classification,
status,
createdAt,
updatedAt,
lastReviewedBy || null,
notes || null
);
}
// Getters
get id() {
return this._id;
}
get packageName() {
return this._packageName;
}
get packageType() {
return this._packageType;
}
get domainId() {
return this._domainId;
}
get classification() {
return this._classification;
}
get status() {
return this._status;
}
get createdAt() {
return this._createdAt;
}
get updatedAt() {
return this._updatedAt;
}
get lastReviewedBy() {
return this._lastReviewedBy;
}
get notes() {
return this._notes;
}
// Business methods
assignToDomain(domainId, reviewedBy) {
this._domainId = domainId;
this._status = "aligned" /* ALIGNED */;
this._lastReviewedBy = reviewedBy || null;
this._updatedAt = /* @__PURE__ */ new Date();
}
markAsOrphaned() {
this._domainId = null;
this._status = "orphaned" /* ORPHANED */;
this._updatedAt = /* @__PURE__ */ new Date();
}
markForReview(reason) {
this._status = "needs_review" /* NEEDS_REVIEW */;
if (reason) {
this._notes = reason;
}
this._updatedAt = /* @__PURE__ */ new Date();
}
markAsDisputed(reason) {
this._status = "disputed" /* DISPUTED */;
this._notes = reason;
this._updatedAt = /* @__PURE__ */ new Date();
}
updateClassification(classification) {
this._classification = classification;
this._updatedAt = /* @__PURE__ */ new Date();
}
addNotes(notes, reviewedBy) {
this._notes = notes;
this._lastReviewedBy = reviewedBy || this._lastReviewedBy;
this._updatedAt = /* @__PURE__ */ new Date();
}
// Domain validation
isWellAligned() {
return this._status === "aligned" /* ALIGNED */ && this._domainId !== null && this._classification.confidence > 0.7;
}
needsAttention() {
return this._status === "orphaned" /* ORPHANED */ || this._status === "needs_review" /* NEEDS_REVIEW */ || this._status === "disputed" /* DISPUTED */ || this._classification.confidence < 0.6;
}
};
exports.PackageType = /* @__PURE__ */ ((PackageType2) => {
PackageType2["DOMAIN"] = "domain";
PackageType2["API"] = "api";
PackageType2["SERVICE"] = "service";
PackageType2["APP"] = "app";
PackageType2["SHARED"] = "shared";
PackageType2["INFRASTRUCTURE"] = "infrastructure";
return PackageType2;
})(exports.PackageType || {});
exports.MappingStatus = /* @__PURE__ */ ((MappingStatus3) => {
MappingStatus3["ALIGNED"] = "aligned";
MappingStatus3["ORPHANED"] = "orphaned";
MappingStatus3["NEEDS_REVIEW"] = "needs_review";
MappingStatus3["DISPUTED"] = "disputed";
return MappingStatus3;
})(exports.MappingStatus || {});
}
});
function getDDDCollection(collectionName) {
const db = persistence.getMongoDb();
return db.collection(collectionName);
}
exports.DEFAULT_DDD_PROJECT_ID = void 0; exports.DDD_COLLECTIONS = void 0; exports.DDD_COLLECTION_METADATA = void 0; exports.DDD_PACKAGE_INFO = void 0; exports.DDD_COLLECTION_MIGRATION_GUIDE = void 0;
var init_collections = __esm({
"src/config/collections.ts"() {
exports.DEFAULT_DDD_PROJECT_ID = "c3f0fb44-79da-4200-b6c8-bd30ab57a6db";
exports.DDD_COLLECTIONS = {
// Strategic Design Bounded Context
DOMAINS: "ddd_domains",
BOUNDED_CONTEXTS: "ddd_bounded_contexts",
// Tactical Design Bounded Context
AGGREGATES: "ddd_aggregates",
// Meta-Model Design Bounded Context
PACKAGE_MAPPINGS: "ddd_package_mappings"
};
exports.DDD_COLLECTION_METADATA = {
[exports.DDD_COLLECTIONS.DOMAINS]: {
boundedContext: "strategic-design",
description: "Business domains representing core business areas and capabilities",
primaryKey: "domainId",
dddLayer: "strategic",
aggregateRoot: "Domain"
},
[exports.DDD_COLLECTIONS.BOUNDED_CONTEXTS]: {
boundedContext: "strategic-design",
description: "Bounded contexts defining clear boundaries for business capabilities",
primaryKey: "boundedContextId",
dddLayer: "strategic",
aggregateRoot: "BoundedContext"
},
[exports.DDD_COLLECTIONS.AGGREGATES]: {
boundedContext: "tactical-design",
description: "Aggregates as consistency boundaries and main building blocks",
primaryKey: "aggregateId",
dddLayer: "tactical",
aggregateRoot: "Aggregate"
},
[exports.DDD_COLLECTIONS.PACKAGE_MAPPINGS]: {
boundedContext: "meta-model-design",
description: "Package-to-domain mappings for monorepo analysis and optimization",
primaryKey: "mappingId",
dddLayer: "implementation",
aggregateRoot: "PackageMapping"
}
};
exports.DDD_PACKAGE_INFO = {
name: "@swoft/ddd",
version: "0.1.0",
description: "Domain-Driven Design strategic and tactical design implementation",
architecture: "Clean Architecture + Domain-Driven Design",
methodology: "Eric Evans Domain-Driven Design",
boundedContexts: [
"Strategic Design",
"Tactical Design",
"Meta-Model Design"
],
dddLayers: [
"Strategic Design (Domains, Bounded Contexts)",
"Tactical Design (Aggregates, Entities, Value Objects)",
"Meta-Model Design (Package Mappings, Analysis)"
],
collections: Object.values(exports.DDD_COLLECTIONS),
databaseDependency: "@swoft/mongo"
};
exports.DDD_COLLECTION_MIGRATION_GUIDE = {
migrations: [
{
from: "ddd_boundedcontexts",
to: exports.DDD_COLLECTIONS.BOUNDED_CONTEXTS,
reason: "Consistent underscore naming convention (bounded_contexts)",
affectedFiles: [
"MongoBoundedContextRepository.ts"
]
}
],
noChangesNeeded: [
{
collection: exports.DDD_COLLECTIONS.DOMAINS,
reason: "Already follows correct naming convention",
files: ["MongoDomainRepository.ts"]
},
{
collection: exports.DDD_COLLECTIONS.AGGREGATES,
reason: "Already follows correct naming convention",
files: ["MongoAggregateRepository.ts"]
},
{
collection: exports.DDD_COLLECTIONS.PACKAGE_MAPPINGS,
reason: "Correctly scoped with ddd_ prefix",
files: ["MongoPackageMappingRepository.ts"]
}
]
};
}
});
// src/bounded-contexts/meta-model-design/infrastructure/repositories/MongoDomainRepository.ts
var MongoDomainRepository_exports = {};
__export(MongoDomainRepository_exports, {
MongoDomainRepository: () => MongoDomainRepository
});
var MongoDomainRepository;
var init_MongoDomainRepository = __esm({
"src/bounded-contexts/meta-model-design/infrastructure/repositories/MongoDomainRepository.ts"() {
init_Domain();
init_collections();
MongoDomainRepository = class {
constructor(database) {
this.database = database;
}
collection;
async getCollection() {
if (!this.collection) {
this.collection = this.database.collection(exports.DDD_COLLECTIONS.DOMAINS);
}
return this.collection;
}
async save(domain) {
const collection = await this.getCollection();
const doc = domain.toJSON();
await collection.replaceOne(
{ domainId: domain.id },
doc,
{ upsert: true }
);
}
async findById(id) {
const collection = await this.getCollection();
const doc = await collection.findOne({ domainId: id });
if (!doc) return null;
return exports.Domain.reconstitute(
{
name: doc.name,
description: doc.description,
vision: doc.vision,
ubiquitousLanguage: doc.ubiquitousLanguage || {}
},
doc.domainId,
new Date(doc.createdAt),
new Date(doc.updatedAt)
);
}
async findByName(name) {
const collection = await this.getCollection();
const doc = await collection.findOne({ name });
if (!doc) return null;
return exports.Domain.reconstitute(
{
name: doc.name,
description: doc.description,
vision: doc.vision,
ubiquitousLanguage: doc.ubiquitousLanguage || {}
},
doc.domainId,
new Date(doc.createdAt),
new Date(doc.updatedAt)
);
}
async findAll(options) {
const collection = await this.getCollection();
const page = options?.page || 1;
const pageSize = options?.pageSize || 100;
const skip = (page - 1) * pageSize;
const query = {};
const conditions = [];
conditions.push({
$or: [
{ projectId: exports.DEFAULT_DDD_PROJECT_ID },
{ projectId: { $exists: false } },
{ projectId: null }
]
});
if (options?.searchTerm) {
conditions.push({
$or: [
{ name: { $regex: options.searchTerm, $options: "i" } },
{ description: { $regex: options.searchTerm, $options: "i" } }
]
});
}
if (conditions.length > 0) {
query.$and = conditions;
}
const [docs, totalCount] = await Promise.all([
collection.find(query).skip(skip).limit(pageSize).toArray(),
collection.countDocuments(query)
]);
const domains = docs.map(
(doc) => exports.Domain.reconstitute(
{
name: doc.name,
description: doc.description,
vision: doc.vision,
ubiquitousLanguage: doc.ubiquitousLanguage || {}
},
doc.domainId,
new Date(doc.createdAt),
new Date(doc.updatedAt)
)
);
return { domains, totalCount };
}
async deleteById(id) {
const collection = await this.getCollection();
await collection.deleteOne({ domainId: id });
}
async existsByName(name) {
const collection = await this.getCollection();
const count = await collection.countDocuments({ name });
return count > 0;
}
};
}
});
// src/bounded-contexts/strategic-design/domain/events/BoundedContextCreatedEvent.ts
var BoundedContextCreatedEvent;
var init_BoundedContextCreatedEvent = __esm({
"src/bounded-contexts/strategic-design/domain/events/BoundedContextCreatedEvent.ts"() {
BoundedContextCreatedEvent = class {
constructor(contextId, domainId, contextName, contextType, authSessionId, initiatedByPersonId, correlationId, version = 1) {
this.contextId = contextId;
this.domainId = domainId;
this.contextName = contextName;
this.contextType = contextType;
this.authSessionId = authSessionId;
this.initiatedByPersonId = initiatedByPersonId;
this.aggregateId = contextId;
this.version = version;
this.timestamp = /* @__PURE__ */ new Date();
this.correlationId = correlationId;
}
type = "BoundedContextCreated";
timestamp;
aggregateId;
version;
correlationId;
};
}
});
// src/bounded-contexts/strategic-design/application/services/BoundedContextCommandService.ts
var BoundedContextCommandService_exports = {};
__export(BoundedContextCommandService_exports, {
BoundedContextCommandService: () => BoundedContextCommandService
});
var BoundedContextCommandService;
var init_BoundedContextCommandService = __esm({
"src/bounded-contexts/strategic-design/application/services/BoundedContextCommandService.ts"() {
init_BoundedContext();
init_BoundedContextCreatedEvent();
init_DDD_SYMBOLS();
BoundedContextCommandService = class {
constructor(boundedContextRepository, domainRepository, eventBus) {
this.boundedContextRepository = boundedContextRepository;
this.domainRepository = domainRepository;
this.eventBus = eventBus;
}
async createBoundedContext(domainId, payload, authSessionId, initiatedByPersonId, correlationId) {
const domain = await this.domainRepository.findById(domainId);
if (!domain) {
throw new Error(`Domain with ID ${domainId} not found`);
}
const existingContext = await this.boundedContextRepository.findByName(
domainId,
payload.name
);
if (existingContext) {
throw new Error(`Bounded context '${payload.name}' already exists in this domain`);
}
const domainServices = (payload.domainServices || []).map((service) => ({
id: crypto.randomUUID(),
name: service.name,
description: service.description
}));
const aggregates = (payload.aggregates || []).map((aggregate) => ({
id: crypto.randomUUID(),
name: aggregate.name,
description: aggregate.description
}));
const valueObjects = (payload.valueObjects || []).map((vo) => ({
id: crypto.randomUUID(),
name: vo.name,
description: vo.description
}));
const entities = (payload.entities || []).map((entity) => ({
id: crypto.randomUUID(),
name: entity.name,
description: entity.description
}));
const boundedContext = payload.id ? exports.BoundedContext.reconstitute({
domainId,
name: payload.name,
description: payload.description,
type: payload.type || "core",
// Use core type for explicitly specified contexts
ubiquitousLanguage: payload.ubiquitousLanguage || {},
domainServices,
aggregates,
valueObjects,
entities
}, payload.id, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()) : exports.BoundedContext.create({
domainId,
name: payload.name,
description: payload.description,
type: payload.type || "supporting",
// Default to supporting for auto-generated contexts
ubiquitousLanguage: payload.ubiquitousLanguage || {},
domainServices,
aggregates,
valueObjects,
entities
});
await this.boundedContextRepository.save(boundedContext);
const boundedContextCreatedEvent = new BoundedContextCreatedEvent(
boundedContext.id,
boundedContext.domainId,
boundedContext.name,
boundedContext.type,
authSessionId,
initiatedByPersonId,
correlationId
);
await this.eventBus.publish(boundedContextCreatedEvent);
return {
boundedContextId: boundedContext.id,
success: true,
message: `Bounded context "${payload.name}" created successfully with ID ${boundedContext.id}`
};
}
async deleteBoundedContext(boundedContextId) {
const exists = !!await this.boundedContextRepository.findById(boundedContextId);
if (!exists) {
throw new Error(`Bounded context not found: ${boundedContextId}`);
}
await this.boundedContextRepository.deleteById(boundedContextId);
}
};
BoundedContextCommandService = __decorateClass([
inversify.injectable(),
__decorateParam(0, inversify.inject(exports.DDD_SYMBOLS.IBoundedContextRepository)),
__decorateParam(1, inversify.inject(exports.DDD_SYMBOLS.IDomainRepository)),
__decorateParam(2, inversify.inject(exports.DDD_SYMBOLS.IEventBus))
], BoundedContextCommandService);
}
});
// src/bounded-contexts/strategic-design/application/services/BoundedContextQueryService.ts
var BoundedContextQueryService_exports = {};
__export(BoundedContextQueryService_exports, {
BoundedContextQueryService: () => BoundedContextQueryService
});
var BoundedContextQueryService;
var init_BoundedContextQueryService = __esm({
"src/bounded-contexts/strategic-design/application/services/BoundedContextQueryService.ts"() {
init_DDD_SYMBOLS();
BoundedContextQueryService = class {
constructor(boundedContextRepository, domainRepository) {
this.boundedContextRepository = boundedContextRepository;
this.domainRepository = domainRepository;
}
async getBoundedContext(domainId, contextId) {
const boundedContext = await this.boundedContextRepository.findById(contextId);
if (!boundedContext) {
return null;
}
if (boundedContext.domainId !== domainId) {
throw new Error(`Bounded context ${contextId} does not belong to domain ${domainId}`);
}
return await this.toDto(boundedContext);
}
async listBoundedContexts(options = {}) {
const page = options.page || 1;
const pageSize = options.pageSize || 10;
let result;
if (options.domainId) {
result = await this.boundedContextRepository.findByDomainId(options.domainId, {
page,
pageSize,
type: options.type,
searchTerm: options.searchTerm
});
} else {
result = await this.boundedContextRepository.findAll({
page,
pageSize,
type: options.type,
searchTerm: options.searchTerm
});
}
const totalPages = Math.ceil(result.totalCount / pageSize);
const contextDtos = await Promise.all(
result.contexts.map((context) => this.toDto(context))
);
return {
items: contextDtos,
totalCount: result.totalCount,
page,
pageSize,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1
};
}
async searchBoundedContexts(searchTerm, domainId) {
const contexts = await this.boundedContextRepository.findByName(domainId, searchTerm);
return contexts ? [await this.toDto(contexts)] : [];
}
async toDto(boundedContext) {
let domainName = "Unknown Domain";
try {
const domain = await this.domainRepository.findById(boundedContext.domainId);
if (domain) {
domainName = domain.name;
}
} catch (error) {
console.warn(`Failed to lookup domain ${boundedContext.domainId}:`, error);
}
return {
id: boundedContext.id,
boundedContextId: boundedContext.id,
// Use domain ID as business identifier
domainId: boundedContext.domainId,
domainName,
// Include the resolved domain name
name: boundedContext.name,
description: boundedContext.description,
type: boundedContext.type,
ubiquitousLanguage: boundedContext.ubiquitousLanguage,
domainServices: boundedContext.domainServices || [],
aggregates: boundedContext.aggregates || [],
valueObjects: boundedContext.valueObjects || [],
entities: boundedContext.entities || [],
createdAt: boundedContext.createdAt,
updatedAt: boundedContext.updatedAt
};
}
};
BoundedContextQueryService = __decorateClass([
inversify.injectable(),
__decorateParam(0, inversify.inject(exports.DDD_SYMBOLS.IBoundedContextRepository)),
__decorateParam(1, inversify.inject(exports.DDD_SYMBOLS.IDomainRepository))
], BoundedContextQueryService);
}
});
// src/bounded-contexts/strategic-design/application/handlers/CreateBoundedContextHandler.ts
var CreateBoundedContextHandler_exports = {};
__export(CreateBoundedContextHandler_exports, {
CreateBoundedContextHandler: () => CreateBoundedContextHandler
});
var CreateBoundedContextHandler;
var init_CreateBoundedContextHandler = __esm({
"src/bounded-contexts/strategic-design/application/handlers/CreateBoundedContextHandler.ts"() {
init_DDD_SYMBOLS();
CreateBoundedContextHandler = class {
constructor(boundedContextCommandService) {
this.boundedContextCommandService = boundedContextCommandService;
}
async handle(command) {
const result = await this.boundedContextCommandService.createBoundedContext(
command.domainId,
command.payload,
command.authSessionId,
command.initiatedByPersonId,
command.correlationId
);
return {
boundedContextId: result.boundedContextId
};
}
};
CreateBoundedContextHandler = __decorateClass([
inversify.injectable(),
__decorateParam(0, inversify.inject(exports.DDD_SYMBOLS.BoundedContextCommandService))
], CreateBoundedContextHandler);
}
});
// src/bounded-contexts/strategic-design/application/handlers/GetBoundedContextHandler.ts
var GetBoundedContextHandler_exports = {};
__export(GetBoundedContextHandler_exports, {
GetBoundedContextHandler: () => GetBoundedContextHandler
});
var GetBoundedContextHandler;
var init_GetBoundedContextHandler = __esm({
"src/bounded-contexts/strategic-design/application/handlers/GetBoundedContextHandler.ts"() {
init_DDD_SYMBOLS();
GetBoundedContextHandler = class {
constructor(boundedContextQueryService) {
this.boundedContextQueryService = boundedContextQueryService;
}
async handle(query) {
const boundedContextDto = await this.boundedContextQueryService.getBoundedContext(
query.domainId,
query.contextId
);
if (!boundedContextDto) {
throw new Error(`Bounded context with ID ${query.contextId} not found`);
}
return boundedContextDto;
}
};
GetBoundedContextHandler = __decorateClass([
inversify.injectable(),
__decorateParam(0, inversify.inject(exports.DDD_SYMBOLS.BoundedContextQueryService))
], GetBoundedContextHandler);
}
});
// src/bounded-contexts/strategic-design/application/handlers/ListBoundedContextsHandler.ts
var ListBoundedContextsHandler_exports = {};
__export(ListBoundedContextsHandler_exports, {
ListBoundedContextsHandler: () => ListBoundedContextsHandler
});
var ListBoundedContextsHandler;
var init_ListBoundedContextsHandler = __esm({
"src/bounded-contexts/strategic-design/application/handlers/ListBoundedContextsHandler.ts"() {
init_DDD_SYMBOLS();
ListBoundedContextsHandler = class {
constructor(boundedContextQueryService) {
this.boundedContextQueryService = boundedContextQueryService;
}
async handle(query) {
return await this.boundedContextQueryService.listBoundedContexts(query.params);
}
};
ListBoundedContextsHandler = __decorateClass([
inversify.injectable(),
__decorateParam(0, inversify.inject(exports.DDD_SYMBOLS.BoundedContextQueryService))
], ListBoundedContextsHandler);
}
});
// src/bounded-contexts/meta-model-design/infrastructure/repositories/MongoPackageMappingRepository.ts
var MongoPackageMappingRepository_exports = {};
__export(MongoPackageMappingRepository_exports, {
MongoPackageMappingRepository: () => MongoPackageMappingRepository
});
var MongoPackageMappingRepository;
var init_MongoPackageMappingRepository = __esm({
"src/bounded-contexts/meta-model-design/infrastructure/repositories/MongoPackageMappingRepository.ts"() {
init_PackageMapping();
init_collections();
MongoPackageMappingRepository = class {
collectionName = exports.DDD_COLLECTIONS.PACKAGE_MAPPINGS;
collection;
getCollection() {
if (!this.collection) {
const db = persistence.getMongoDb();
this.collection = db.collection(this.collectionName);
}
return this.collection;
}
async save(mapping) {
const document = this.toDocument(mapping);
const collection = this.getCollection();
await collection.replaceOne(
{ packageName: mapping.packageName },
document,
{ upsert: true }
);
}
async findByPackageName(packageName) {
const collection = this.getCollection();
const document = await collection.findOne({ packageName });
return document ? this.toDomain(document) : null;
}
async findByDomainId(domainId) {
const documents = await this.collection.find({ domainId }).toArray();
return documents.map((doc) => this.toDomain(doc));
}
async findByStatus(status) {
const documents = await this.collection.find({ status }).toArray();
return documents.map((doc) => this.toDomain(doc));
}
async findByPackageType(packageType) {
const documents = await this.collection.find({ packageType }).toArray();
return documents.map((doc) => this.toDomain(doc));
}
async findAll(filters) {
const query = {};
if (filters.domainId) {
query.domainId = filters.domainId;
}
if (filters.status) {
query.status = filters.status;
}
if (filters.packageType) {
query.packageType = filters.packageType;
}
if (filters.search) {
query.packageName = { $regex: filters.search, $options: "i" };
}
const total = await this.collection.countDocuments(query);
const documents = await this.collection.find(query).sort({ updatedAt: -1 }).skip(filters.offset || 0).limit(filters.limit || 50).toArray();
const mappings = documents.map((doc) => this.toDomain(doc));
return { mappings, total };
}
async deleteByPackageName(packageName) {
await this.collection.deleteOne({ packageName });
}
async bulkSave(mappings) {
if (mappings.length === 0) return;
const operations = mappings.map((mapping) => ({
replaceOne: {
filter: { packageName: mapping.packageName },
replacement: this.toDocument(mapping),
upsert: true
}
}));
await this.collection.bulkWrite(operations);
}
async exists(packageName) {
const count = await this.collection.countDocuments({ packageName });
return count > 0;
}
async getStatistics() {
const pipeline = [
{
$group: {
_id: null,
totalMappings: { $sum: 1 },
alignedCount: {
$sum: { $cond: [{ $eq: ["$status", "aligned"] }, 1, 0] }
},
orphanedCount: {
$sum: { $cond: [{ $eq: ["$status", "orphaned"] }, 1, 0] }
},
needsReviewCount: {
$sum: { $cond: [{ $eq: ["$status", "needs_review"] }, 1, 0] }
},
disputedCount: {
$sum: { $cond: [{ $eq: ["$status", "disputed"] }, 1, 0] }
},
averageConfidence: { $avg: "$classification.confidence" }
}
}
];
const [stats] = await this.collection.aggregate(pipeline).toArray();
const typeDistribution = await this.collection.aggregate([
{
$group: {
_id: "$packageType",
count: { $sum: 1 }
}
}
]).toArray();
const byPackageType = {
["domain" /* DOMAIN */]: 0,
["api" /* API */]: 0,
["service" /* SERVICE */]: 0,
["app" /* APP */]: 0,
["shared" /* SHARED */]: 0,
["infrastructure" /* INFRASTRUCTURE */]: 0
};
typeDistribution.forEach((item) => {
if (item._id && Object.values(exports.PackageType).includes(item._id)) {
byPackageType[item._id] = item.count;
}
});
return {
totalMappings: stats?.totalMappings || 0,
alignedCount: stats?.alignedCount || 0,
orphanedCount: stats?.orphanedCount || 0,
needsReviewCount: stats?.needsReviewCount || 0,
disputedCount: stats?.disputedCount || 0,
byPackageType,
averageConfidence: stats?.averageConfidence || 0
};
}
// Private mapping methods
toDocument(mapping) {
return {
_id: mapping.id,
packageName: mapping.packageName,
packageType: mapping.packageType,
domainId: mapping.domainId,
classification: mapping.classification,
status: mapping.status,
createdAt: mapping.createdAt,
updatedAt: mapping.updatedAt,
lastReviewedBy: mapping.lastReviewedBy,
notes: mapping.notes
};
}
toDomain(document) {
return exports.PackageMapping.reconstitute(
document._id,
document.packageName,
document.packageType,
document.domainId,
document.classification,
document.status,
document.createdAt,
document.updatedAt,
document.lastReviewedBy || void 0,
document.notes || void 0
);
}
};
}
});
// src/bounded-contexts/ai-tools/ports/PersonLookupPort.ts
var PersonLookupError;
var init_PersonLookupPort = __esm({
"src/bounded-contexts/ai-tools/ports/PersonLookupPort.ts"() {
PersonLookupError = class extends Error {
constructor(message, cause, operation) {
super(message);
this.cause = cause;
this.operation = operation;
this.name = "PersonLookupError";
}
};
}
});
// src/bounded-contexts/ai-tools/infrastructure/adapters/PersonLookupAdapter.ts
var PersonLookupAdapter_exports = {};
__export(PersonLookupAdapter_exports, {
PersonLookupAdapter: () => PersonLookupAdapter,
createPersonLookupAdapter: () => createPersonLookupAdapter
});
function createPersonLookupAdapter(implementation, config) {
switch (implementation) {
case "http":
return new PersonLookupAdapter({
...config,
apiBaseUrl: config?.apiBaseUrl || "http://localhost:3000"
});
case "database":
return new PersonLookupAdapter({
...config
// Database-specific config
});
case "eventstore":
return new PersonLookupAdapter({
...config
// Event store-specific config
});
case "mock":
return new PersonLookupAdapter({
...config,
enableCaching: false
});
default:
return new PersonLookupAdapter(config);
}
}
var PersonLookupAdapter;
var init_PersonLookupAdapter = __esm({
"src/bounded-contexts/ai-tools/infrastructure/adapters/PersonLookupAdapter.ts"() {
init_PersonLookupPort();
PersonLookupAdapter = class {
constructor(config = {}) {
this.config = config;
}
cache = /* @__PURE__ */ new Map();
/**
* Find a person by their ID
* Translates from PersonSummary (Party Manager) to PersonReference (DDD)
*/
async findPersonById(personId) {
try {
if (this.config.enableCaching) {
const cached = this.getFromCache(personId);
if (cached) return cached;
}
return null;
} catch (error) {
throw new PersonLookupError(
`Failed to find person by ID: ${personId}`,
error,
"findPersonById"
);
}
}
/**
* Find all persons in a specific department
*/
async findPersonsByDepartment(department) {
try {
return [];
} catch (error) {
throw new PersonLookupError(
`Failed to find persons by department: ${department}`,
error,
"findPersonsByDepartment"
);
}
}
/**
* Find persons by role type (Human vs AiAgent)
*/
async findPersonsByRoleType(roleType) {
try {
return [];
} catch (error) {
thro