UNPKG

@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
'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