UI styling improvements: dashboard headers and navigation
- Restore blue PageHeader on Dashboard (/app-components) - Update homepage (/) with subtle header design without blue bar - Add uniform PageHeader styling to application edit page - Fix Rapporten link on homepage to point to /reports overview - Improve header descriptions spacing for better readability
This commit is contained in:
801
docs/refactor-plan.md
Normal file
801
docs/refactor-plan.md
Normal file
@@ -0,0 +1,801 @@
|
||||
# Refactor Plan - Phase 1: Architecture Analysis
|
||||
|
||||
**Created:** 2025-01-XX
|
||||
**Status:** Phase 1 - Analysis Only (No functional changes)
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document provides a comprehensive analysis of the current architecture and a plan for refactoring the CMDB Insight codebase to improve maintainability, reduce duplication, and establish clearer separation of concerns.
|
||||
|
||||
**Scope:** This is Phase 1 - analysis and planning only. No code changes will be made in this phase.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Current Architecture Map](#current-architecture-map)
|
||||
2. [Pain Points & Duplication](#pain-points--duplication)
|
||||
3. [Target Architecture](#target-architecture)
|
||||
4. [Migration Steps](#migration-steps)
|
||||
5. [Explicit Deletion List](#explicit-deletion-list)
|
||||
6. [API Payload Contract & Recursion Insights](#api-payload-contract--recursion-insights)
|
||||
|
||||
---
|
||||
|
||||
## Current Architecture Map
|
||||
|
||||
### File/Folder Structure
|
||||
|
||||
```
|
||||
backend/src/
|
||||
├── services/
|
||||
│ ├── jiraAssets.ts # High-level Jira Assets service (business logic, ~3454 lines)
|
||||
│ ├── jiraAssetsClient.ts # Low-level Jira Assets API client (~646 lines)
|
||||
│ ├── schemaDiscoveryService.ts # Discovers schema from Jira API (~520 lines)
|
||||
│ ├── schemaCacheService.ts # Caches schema metadata
|
||||
│ ├── schemaConfigurationService.ts # Manages enabled object types
|
||||
│ ├── schemaMappingService.ts # Maps object types to schema IDs
|
||||
│ ├── syncEngine.ts # Background sync service (full/incremental) (~630 lines)
|
||||
│ ├── normalizedCacheStore.ts # EAV pattern DB store (~1695 lines)
|
||||
│ ├── cmdbService.ts # Universal schema-driven CMDB service (~531 lines)
|
||||
│ ├── queryBuilder.ts # Dynamic SQL query builder (~278 lines)
|
||||
│ ├── cacheStore.old.ts # Legacy cache store (deprecated)
|
||||
│ └── database/
|
||||
│ ├── normalized-schema.ts # DB schema definitions (Postgres/SQLite)
|
||||
│ ├── factory.ts # Database adapter factory
|
||||
│ ├── interface.ts # Database adapter interface
|
||||
│ ├── postgresAdapter.ts # PostgreSQL adapter
|
||||
│ ├── sqliteAdapter.ts # SQLite adapter
|
||||
│ ├── migrate-to-normalized-schema.ts
|
||||
│ └── fix-object-types-constraints.ts
|
||||
├── routes/
|
||||
│ ├── applications.ts # Application-specific endpoints (~780 lines)
|
||||
│ ├── objects.ts # Generic object endpoints (~185 lines)
|
||||
│ ├── cache.ts # Cache/sync endpoints (~165 lines)
|
||||
│ ├── schema.ts # Schema endpoints (~107 lines)
|
||||
│ └── schemaConfiguration.ts # Schema configuration endpoints
|
||||
├── generated/
|
||||
│ ├── jira-types.ts # Generated TypeScript types (~934 lines)
|
||||
│ └── jira-schema.ts # Generated schema metadata (~895 lines)
|
||||
└── scripts/
|
||||
├── discover-schema.ts # Schema discovery CLI
|
||||
├── generate-types-from-db.ts # Type generation from DB (~485 lines)
|
||||
└── generate-schema.ts # Legacy schema generation
|
||||
```
|
||||
|
||||
### Module Responsibilities
|
||||
|
||||
#### 1. Jira Assets API Client Calls
|
||||
|
||||
**Primary Files:**
|
||||
- `services/jiraAssetsClient.ts` - Low-level HTTP client
|
||||
- Methods: `getObject()`, `searchObjects()`, `getAllObjectsOfType()`, `updateObject()`, `parseObject()`
|
||||
- Handles authentication (service account token for reads, user PAT for writes)
|
||||
- API detection (Data Center vs Cloud)
|
||||
- Object parsing from Jira format to CMDB format
|
||||
|
||||
- `services/jiraAssets.ts` - High-level business logic wrapper
|
||||
- Application-specific methods (e.g., `getApplications()`, `updateApplication()`)
|
||||
- Dashboard data aggregation
|
||||
- Reference data caching
|
||||
- Team dashboard calculations
|
||||
- Legacy API methods
|
||||
|
||||
**Dependencies:**
|
||||
- Uses `schemaCacheService` for type lookups
|
||||
- Uses `schemaMappingService` for schema ID resolution
|
||||
|
||||
#### 2. Schema Discovery/Sync
|
||||
|
||||
**Primary Files:**
|
||||
- `services/schemaDiscoveryService.ts`
|
||||
- Discovers object types from Jira API (`/objectschema/{id}/objecttypes/flat`)
|
||||
- Discovers attributes for each object type (`/objecttype/{id}/attributes`)
|
||||
- Stores schema in database (`object_types`, `attributes` tables)
|
||||
- Provides lookup methods: `getAttribute()`, `getAttributesForType()`, `getObjectType()`
|
||||
|
||||
- `services/schemaCacheService.ts`
|
||||
- Caches schema from database
|
||||
- Provides runtime schema access
|
||||
|
||||
- `services/schemaConfigurationService.ts`
|
||||
- Manages enabled/disabled object types
|
||||
- Schema-to-object-type mapping
|
||||
- Configuration validation
|
||||
|
||||
- `services/schemaMappingService.ts`
|
||||
- Maps object type names to schema IDs
|
||||
- Legacy compatibility
|
||||
|
||||
**Scripts:**
|
||||
- `scripts/discover-schema.ts` - CLI tool to trigger schema discovery
|
||||
- `scripts/generate-types-from-db.ts` - Generates TypeScript types from database
|
||||
|
||||
#### 3. Object Sync/Import
|
||||
|
||||
**Primary Files:**
|
||||
- `services/syncEngine.ts`
|
||||
- `fullSync()` - Syncs all enabled object types
|
||||
- `incrementalSync()` - Periodic sync of updated objects
|
||||
- `syncType()` - Sync single object type
|
||||
- `syncObject()` - Sync single object
|
||||
- Uses `jiraAssetsClient.getAllObjectsOfType()` for fetching
|
||||
- Uses `normalizedCacheStore.batchUpsertObjects()` for storage
|
||||
|
||||
**Flow:**
|
||||
1. Fetch objects from Jira via `jiraAssetsClient`
|
||||
2. Parse objects via `jiraAssetsClient.parseObject()`
|
||||
3. Store objects via `normalizedCacheStore.batchUpsertObjects()`
|
||||
4. Extract relations via `normalizedCacheStore.extractAndStoreRelations()`
|
||||
|
||||
#### 4. DB Normalization Store (EAV)
|
||||
|
||||
**Primary Files:**
|
||||
- `services/normalizedCacheStore.ts` (~1695 lines)
|
||||
- **Storage:** `normalizeObject()`, `batchUpsertObjects()`, `upsertObject()`
|
||||
- **Retrieval:** `getObject()`, `getObjects()`, `reconstructObject()`, `loadAttributeValues()`
|
||||
- **Relations:** `extractAndStoreRelations()`, `getRelatedObjects()`, `getReferencingObjects()`
|
||||
- **Query:** `queryWithFilters()` (uses `queryBuilder`)
|
||||
|
||||
- `services/database/normalized-schema.ts`
|
||||
- Defines EAV schema: `objects`, `attributes`, `attribute_values`, `object_relations`
|
||||
|
||||
**EAV Pattern:**
|
||||
- `objects` table: Minimal metadata (id, objectKey, label, type, timestamps)
|
||||
- `attributes` table: Schema metadata (jira_attr_id, field_name, type, is_multiple, etc.)
|
||||
- `attribute_values` table: Actual values (text_value, number_value, boolean_value, reference_object_id, array_index)
|
||||
- `object_relations` table: Extracted relationships (source_id, target_id, attribute_id)
|
||||
|
||||
#### 5. Backend API Endpoints
|
||||
|
||||
**Primary Files:**
|
||||
- `routes/applications.ts` - Application-specific endpoints
|
||||
- `POST /applications/search` - Search with filters
|
||||
- `GET /applications/:id` - Get application details
|
||||
- `PUT /applications/:id` - Update application
|
||||
- `GET /applications/:id/related/:type` - Get related objects
|
||||
- Dashboard endpoints (`/team-dashboard`, `/team-portfolio-health`)
|
||||
|
||||
- `routes/objects.ts` - Generic object endpoints
|
||||
- `GET /objects` - List supported types
|
||||
- `GET /objects/:type` - Get all objects of type
|
||||
- `GET /objects/:type/:id` - Get single object
|
||||
- `GET /objects/:type/:id/related/:relationType` - Get related objects
|
||||
|
||||
- `routes/cache.ts` - Cache management
|
||||
- `POST /cache/sync` - Trigger full sync
|
||||
- `POST /cache/sync/:objectType` - Sync single type
|
||||
- `POST /cache/refresh-application/:id` - Refresh single object
|
||||
|
||||
- `routes/schema.ts` - Schema endpoints
|
||||
- `GET /schema` - Get schema metadata
|
||||
- `GET /schema/types` - List object types
|
||||
- `GET /schema/types/:type` - Get type definition
|
||||
|
||||
**Service Layer:**
|
||||
- Routes delegate to `cmdbService`, `dataService`, `syncEngine`
|
||||
- `cmdbService` provides unified interface (read/write with conflict detection)
|
||||
- `dataService` provides application-specific business logic
|
||||
|
||||
#### 6. Query Builder (Object Reconstruction)
|
||||
|
||||
**Primary Files:**
|
||||
- `services/queryBuilder.ts`
|
||||
- `buildWhereClause()` - Builds WHERE conditions from filters
|
||||
- `buildFilterCondition()` - Handles different attribute types (text, reference, number, etc.)
|
||||
- `buildOrderBy()` - ORDER BY clause
|
||||
- `buildPagination()` - LIMIT/OFFSET clause
|
||||
|
||||
**Usage:**
|
||||
- Used by `normalizedCacheStore.queryWithFilters()` to build dynamic SQL
|
||||
- Handles complex filters (exact match, exists, contains, reference filters)
|
||||
|
||||
#### 7. Generated Types/Reflect Scripts
|
||||
|
||||
**Primary Files:**
|
||||
- `scripts/generate-types-from-db.ts`
|
||||
- Reads from `object_types` and `attributes` tables
|
||||
- Generates `generated/jira-types.ts` (TypeScript interfaces)
|
||||
- Generates `generated/jira-schema.ts` (Schema metadata with lookup maps)
|
||||
|
||||
**Generated Output:**
|
||||
- `jira-types.ts`: TypeScript interfaces for each object type (e.g., `ApplicationComponent`, `Server`)
|
||||
- `jira-schema.ts`: `OBJECT_TYPES` record, lookup maps (`TYPE_ID_TO_NAME`, `JIRA_NAME_TO_TYPE`), helper functions
|
||||
|
||||
---
|
||||
|
||||
## Pain Points & Duplication
|
||||
|
||||
### 1. Dual API Clients (jiraAssets.ts vs jiraAssetsClient.ts)
|
||||
|
||||
**Issue:** Two separate services handling Jira API calls:
|
||||
- `jiraAssetsClient.ts` - Low-level, focused on API communication
|
||||
- `jiraAssets.ts` - High-level, contains business logic + API calls
|
||||
|
||||
**Problems:**
|
||||
- Duplication of API request logic
|
||||
- Inconsistent error handling
|
||||
- Mixed concerns (business logic + infrastructure)
|
||||
- `jiraAssets.ts` is huge (~3454 lines) and hard to maintain
|
||||
|
||||
**Location:**
|
||||
- `backend/src/services/jiraAssets.ts` - Contains both API calls and business logic
|
||||
- `backend/src/services/jiraAssetsClient.ts` - Clean separation but incomplete
|
||||
|
||||
### 2. Schema Discovery/Caching Duplication
|
||||
|
||||
**Issue:** Multiple services handling schema metadata:
|
||||
- `schemaDiscoveryService.ts` - Discovers and stores schema
|
||||
- `schemaCacheService.ts` - Caches schema from DB
|
||||
- `schemaConfigurationService.ts` - Manages enabled types
|
||||
- `schemaMappingService.ts` - Maps types to schema IDs
|
||||
|
||||
**Problems:**
|
||||
- Unclear boundaries between services
|
||||
- Potential for stale cache
|
||||
- Complex initialization dependencies
|
||||
|
||||
**Location:**
|
||||
- `backend/src/services/schema*.ts` files
|
||||
|
||||
### 3. Mixed Responsibilities in normalizedCacheStore.ts
|
||||
|
||||
**Issue:** Large file (~1695 lines) handling multiple concerns:
|
||||
- Database operations (EAV storage/retrieval)
|
||||
- Object reconstruction (TypeScript object building)
|
||||
- Reference resolution (fetching missing referenced objects)
|
||||
- Relation extraction
|
||||
|
||||
**Problems:**
|
||||
- Hard to test individual concerns
|
||||
- Difficult to optimize specific operations
|
||||
- Violates single responsibility principle
|
||||
|
||||
**Location:**
|
||||
- `backend/src/services/normalizedCacheStore.ts`
|
||||
|
||||
### 4. Application-Specific Logic in Generic Services
|
||||
|
||||
**Issue:** Application-specific business logic scattered:
|
||||
- `routes/applications.ts` - Application-specific endpoints (~780 lines)
|
||||
- `services/dataService.ts` - Application business logic
|
||||
- `services/jiraAssets.ts` - Application aggregation logic
|
||||
- `services/cmdbService.ts` - Generic service used by applications
|
||||
|
||||
**Problems:**
|
||||
- Hard to extend to other object types
|
||||
- Tight coupling between routes and services
|
||||
- Business logic mixed with data access
|
||||
|
||||
**Location:**
|
||||
- `backend/src/routes/applications.ts`
|
||||
- `backend/src/services/dataService.ts`
|
||||
|
||||
### 5. Type Generation Pipeline Complexity
|
||||
|
||||
**Issue:** Multiple scripts and services involved in type generation:
|
||||
- `scripts/discover-schema.ts` - Triggers schema discovery
|
||||
- `services/schemaDiscoveryService.ts` - Discovers schema
|
||||
- `scripts/generate-types-from-db.ts` - Generates TypeScript files
|
||||
- `generated/jira-types.ts` - Generated output (must be regenerated when schema changes)
|
||||
|
||||
**Problems:**
|
||||
- Unclear workflow
|
||||
- Manual steps required
|
||||
- Generated files can get out of sync
|
||||
|
||||
**Location:**
|
||||
- `backend/scripts/discover-schema.ts`
|
||||
- `backend/scripts/generate-types-from-db.ts`
|
||||
- `backend/src/generated/*.ts`
|
||||
|
||||
### 6. Legacy Code (cacheStore.old.ts)
|
||||
|
||||
**Issue:** Old cache store still present in codebase:
|
||||
- `services/cacheStore.old.ts` - Deprecated implementation
|
||||
|
||||
**Problems:**
|
||||
- Confusing for new developers
|
||||
- Takes up space
|
||||
- No longer used
|
||||
|
||||
**Location:**
|
||||
- `backend/src/services/cacheStore.old.ts`
|
||||
|
||||
### 7. Inconsistent Error Handling
|
||||
|
||||
**Issue:** Different error handling patterns across services:
|
||||
- Some use try/catch with logger
|
||||
- Some throw errors
|
||||
- Some return null/undefined
|
||||
- Inconsistent error messages
|
||||
|
||||
**Problems:**
|
||||
- Hard to debug issues
|
||||
- Inconsistent API responses
|
||||
- No centralized error handling
|
||||
|
||||
**Location:**
|
||||
- Throughout codebase
|
||||
|
||||
---
|
||||
|
||||
## Target Architecture
|
||||
|
||||
### Domain/Infrastructure/Services/API Separation
|
||||
|
||||
```
|
||||
backend/src/
|
||||
├── domain/ # Domain models & business logic
|
||||
│ ├── cmdb/
|
||||
│ │ ├── Object.ts # CMDBObject base interface
|
||||
│ │ ├── ObjectType.ts # ObjectTypeDefinition
|
||||
│ │ ├── Attribute.ts # AttributeDefinition
|
||||
│ │ └── Reference.ts # ObjectReference
|
||||
│ ├── schema/
|
||||
│ │ ├── Schema.ts # Schema domain model
|
||||
│ │ └── SchemaDiscovery.ts # Schema discovery business logic
|
||||
│ └── sync/
|
||||
│ ├── SyncEngine.ts # Sync orchestration logic
|
||||
│ └── SyncStrategy.ts # Sync strategies (full, incremental)
|
||||
│
|
||||
├── infrastructure/ # External integrations & infrastructure
|
||||
│ ├── jira/
|
||||
│ │ ├── JiraAssetsClient.ts # Low-level HTTP client (pure API calls)
|
||||
│ │ ├── JiraAssetsApi.ts # API contract definitions
|
||||
│ │ └── JiraResponseParser.ts # Response parsing utilities
|
||||
│ └── database/
|
||||
│ ├── adapters/ # Database adapters (Postgres, SQLite)
|
||||
│ ├── schema/ # Schema definitions
|
||||
│ └── migrations/ # Database migrations
|
||||
│
|
||||
├── services/ # Application services (use cases)
|
||||
│ ├── cmdb/
|
||||
│ │ ├── CmdbReadService.ts # Read operations
|
||||
│ │ ├── CmdbWriteService.ts # Write operations with conflict detection
|
||||
│ │ └── CmdbQueryService.ts # Query operations
|
||||
│ ├── schema/
|
||||
│ │ ├── SchemaService.ts # Schema CRUD operations
|
||||
│ │ └── SchemaDiscoveryService.ts # Schema discovery orchestration
|
||||
│ └── sync/
|
||||
│ └── SyncService.ts # Sync orchestration
|
||||
│
|
||||
├── repositories/ # Data access layer
|
||||
│ ├── ObjectRepository.ts # Object CRUD (uses EAV store)
|
||||
│ ├── AttributeRepository.ts # Attribute value access
|
||||
│ ├── RelationRepository.ts # Relationship access
|
||||
│ └── SchemaRepository.ts # Schema metadata access
|
||||
│
|
||||
├── stores/ # Storage implementations
|
||||
│ ├── NormalizedObjectStore.ts # EAV pattern implementation
|
||||
│ ├── ObjectReconstructor.ts # Object reconstruction from EAV
|
||||
│ └── RelationExtractor.ts # Relation extraction logic
|
||||
│
|
||||
├── api/ # HTTP API layer
|
||||
│ ├── routes/
|
||||
│ │ ├── objects.ts # Generic object endpoints
|
||||
│ │ ├── schema.ts # Schema endpoints
|
||||
│ │ └── sync.ts # Sync endpoints
|
||||
│ ├── handlers/ # Request handlers (thin layer)
|
||||
│ │ ├── ObjectHandler.ts
|
||||
│ │ ├── SchemaHandler.ts
|
||||
│ │ └── SyncHandler.ts
|
||||
│ └── middleware/ # Auth, validation, etc.
|
||||
│
|
||||
├── queries/ # Query builders
|
||||
│ ├── ObjectQueryBuilder.ts # SQL query construction
|
||||
│ └── FilterBuilder.ts # Filter condition builder
|
||||
│
|
||||
└── scripts/ # CLI tools
|
||||
├── discover-schema.ts # Schema discovery CLI
|
||||
└── generate-types.ts # Type generation CLI
|
||||
```
|
||||
|
||||
### Key Principles
|
||||
|
||||
1. **Domain Layer**: Pure business logic, no infrastructure dependencies
|
||||
2. **Infrastructure Layer**: External integrations (Jira API, database)
|
||||
3. **Services Layer**: Orchestrates domain logic and infrastructure
|
||||
4. **Repository Layer**: Data access abstraction
|
||||
5. **Store Layer**: Storage implementations (EAV, caching)
|
||||
6. **API Layer**: Thin HTTP handlers that delegate to services
|
||||
|
||||
---
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Extract Jira API Client (Infrastructure)
|
||||
|
||||
**Goal:** Create pure infrastructure client with no business logic
|
||||
|
||||
1. Consolidate `jiraAssetsClient.ts` and `jiraAssets.ts` API methods into single `JiraAssetsClient`
|
||||
2. Extract API contract types to `infrastructure/jira/JiraAssetsApi.ts`
|
||||
3. Move response parsing to `infrastructure/jira/JiraResponseParser.ts`
|
||||
4. Remove business logic from API client (delegate to services)
|
||||
|
||||
**Files to Create:**
|
||||
- `infrastructure/jira/JiraAssetsClient.ts`
|
||||
- `infrastructure/jira/JiraAssetsApi.ts`
|
||||
- `infrastructure/jira/JiraResponseParser.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/jiraAssets.ts` - Remove API calls, keep business logic
|
||||
- `services/jiraAssetsClient.ts` - Merge into infrastructure client
|
||||
|
||||
**Files to Delete:**
|
||||
- None (yet - deprecate old files after migration)
|
||||
|
||||
### Step 2: Extract Schema Domain & Services
|
||||
|
||||
**Goal:** Separate schema discovery business logic from infrastructure
|
||||
|
||||
1. Create `domain/schema/` with domain models
|
||||
2. Move schema discovery logic to `services/schema/SchemaDiscoveryService.ts`
|
||||
3. Consolidate schema caching in `services/schema/SchemaService.ts`
|
||||
4. Remove duplication between `schemaCacheService`, `schemaConfigurationService`, `schemaMappingService`
|
||||
|
||||
**Files to Create:**
|
||||
- `domain/schema/Schema.ts`
|
||||
- `services/schema/SchemaService.ts`
|
||||
- `services/schema/SchemaDiscoveryService.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/schemaDiscoveryService.ts` - Split into domain + service
|
||||
- `services/schemaCacheService.ts` - Merge into SchemaService
|
||||
- `services/schemaConfigurationService.ts` - Merge into SchemaService
|
||||
- `services/schemaMappingService.ts` - Merge into SchemaService
|
||||
|
||||
**Files to Delete:**
|
||||
- `services/schemaCacheService.ts` (after merge)
|
||||
- `services/schemaMappingService.ts` (after merge)
|
||||
|
||||
### Step 3: Extract Repository Layer
|
||||
|
||||
**Goal:** Abstract data access from business logic
|
||||
|
||||
1. Create `repositories/ObjectRepository.ts` - Interface for object CRUD
|
||||
2. Create `repositories/AttributeRepository.ts` - Interface for attribute access
|
||||
3. Create `repositories/RelationRepository.ts` - Interface for relationships
|
||||
4. Implement repositories using `NormalizedObjectStore`
|
||||
|
||||
**Files to Create:**
|
||||
- `repositories/ObjectRepository.ts`
|
||||
- `repositories/AttributeRepository.ts`
|
||||
- `repositories/RelationRepository.ts`
|
||||
- `repositories/SchemaRepository.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/normalizedCacheStore.ts` - Extract repository implementations
|
||||
|
||||
### Step 4: Extract Store Implementations
|
||||
|
||||
**Goal:** Separate storage implementations from business logic
|
||||
|
||||
1. Extract EAV storage to `stores/NormalizedObjectStore.ts`
|
||||
2. Extract object reconstruction to `stores/ObjectReconstructor.ts`
|
||||
3. Extract relation extraction to `stores/RelationExtractor.ts`
|
||||
|
||||
**Files to Create:**
|
||||
- `stores/NormalizedObjectStore.ts` - EAV storage/retrieval
|
||||
- `stores/ObjectReconstructor.ts` - TypeScript object reconstruction
|
||||
- `stores/RelationExtractor.ts` - Relation extraction from objects
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/normalizedCacheStore.ts` - Split into store classes
|
||||
|
||||
### Step 5: Extract Query Builders
|
||||
|
||||
**Goal:** Separate query construction from execution
|
||||
|
||||
1. Move `queryBuilder.ts` to `queries/ObjectQueryBuilder.ts`
|
||||
2. Extract filter building to `queries/FilterBuilder.ts`
|
||||
|
||||
**Files to Create:**
|
||||
- `queries/ObjectQueryBuilder.ts`
|
||||
- `queries/FilterBuilder.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/queryBuilder.ts` - Move to queries/
|
||||
|
||||
### Step 6: Extract CMDB Services
|
||||
|
||||
**Goal:** Separate read/write/query concerns
|
||||
|
||||
1. Create `services/cmdb/CmdbReadService.ts` - Read operations
|
||||
2. Create `services/cmdb/CmdbWriteService.ts` - Write operations with conflict detection
|
||||
3. Create `services/cmdb/CmdbQueryService.ts` - Query operations
|
||||
|
||||
**Files to Create:**
|
||||
- `services/cmdb/CmdbReadService.ts`
|
||||
- `services/cmdb/CmdbWriteService.ts`
|
||||
- `services/cmdb/CmdbQueryService.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/cmdbService.ts` - Split into read/write/query services
|
||||
|
||||
### Step 7: Extract Sync Service
|
||||
|
||||
**Goal:** Separate sync orchestration from storage
|
||||
|
||||
1. Create `domain/sync/SyncEngine.ts` - Sync business logic
|
||||
2. Create `services/sync/SyncService.ts` - Sync orchestration
|
||||
|
||||
**Files to Create:**
|
||||
- `domain/sync/SyncEngine.ts`
|
||||
- `services/sync/SyncService.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `services/syncEngine.ts` - Split into domain + service
|
||||
|
||||
### Step 8: Refactor API Routes
|
||||
|
||||
**Goal:** Thin HTTP handlers delegating to services
|
||||
|
||||
1. Create `api/handlers/` directory
|
||||
2. Move route logic to handlers
|
||||
3. Routes become thin wrappers around handlers
|
||||
|
||||
**Files to Create:**
|
||||
- `api/handlers/ObjectHandler.ts`
|
||||
- `api/handlers/SchemaHandler.ts`
|
||||
- `api/handlers/SyncHandler.ts`
|
||||
|
||||
**Files to Modify:**
|
||||
- `routes/applications.ts` - Extract handlers
|
||||
- `routes/objects.ts` - Extract handlers
|
||||
- `routes/cache.ts` - Extract handlers
|
||||
- `routes/schema.ts` - Extract handlers
|
||||
|
||||
### Step 9: Clean Up Legacy Code
|
||||
|
||||
**Goal:** Remove deprecated files
|
||||
|
||||
**Files to Delete:**
|
||||
- `services/cacheStore.old.ts`
|
||||
- Deprecated service files after migration complete
|
||||
|
||||
### Step 10: Update Type Generation
|
||||
|
||||
**Goal:** Simplify type generation workflow
|
||||
|
||||
1. Consolidate type generation logic
|
||||
2. Add automatic type generation on schema discovery
|
||||
3. Update documentation
|
||||
|
||||
**Files to Modify:**
|
||||
- `scripts/generate-types-from-db.ts` - Enhance with auto-discovery
|
||||
- `scripts/discover-schema.ts` - Auto-generate types after discovery
|
||||
|
||||
---
|
||||
|
||||
## Explicit Deletion List
|
||||
|
||||
### Phase 2 (After Migration Complete)
|
||||
|
||||
1. **`backend/src/services/cacheStore.old.ts`**
|
||||
- Reason: Legacy implementation, replaced by `normalizedCacheStore.ts`
|
||||
- Deprecation date: TBD
|
||||
|
||||
2. **`backend/src/services/jiraAssets.ts`** (after extracting business logic)
|
||||
- Reason: API calls moved to infrastructure layer, business logic to services
|
||||
- Replacement: `infrastructure/jira/JiraAssetsClient.ts` + `services/cmdb/Cmdb*Service.ts`
|
||||
|
||||
3. **`backend/src/services/schemaCacheService.ts`** (after consolidation)
|
||||
- Reason: Merged into `services/schema/SchemaService.ts`
|
||||
|
||||
4. **`backend/src/services/schemaMappingService.ts`** (after consolidation)
|
||||
- Reason: Merged into `services/schema/SchemaService.ts`
|
||||
|
||||
5. **`backend/scripts/generate-schema.ts`** (if still present)
|
||||
- Reason: Replaced by `generate-types-from-db.ts`
|
||||
|
||||
### Notes
|
||||
|
||||
- Keep old files until migration is complete and tested
|
||||
- Mark as deprecated with `@deprecated` JSDoc comments
|
||||
- Add migration guide for each deprecated file
|
||||
|
||||
---
|
||||
|
||||
## API Payload Contract & Recursion Insights
|
||||
|
||||
### Jira Assets API Payload Structure
|
||||
|
||||
The Jira Assets API returns objects with the following nested structure:
|
||||
|
||||
```typescript
|
||||
interface JiraAssetsSearchResponse {
|
||||
objectEntries: JiraAssetsObject[]; // Top-level array of objects
|
||||
// ... pagination metadata
|
||||
}
|
||||
|
||||
interface JiraAssetsObject {
|
||||
id: number;
|
||||
objectKey: string;
|
||||
label: string;
|
||||
objectType: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
attributes: JiraAssetsAttribute[]; // Array of attributes
|
||||
updated?: string;
|
||||
created?: string;
|
||||
}
|
||||
|
||||
interface JiraAssetsAttribute {
|
||||
objectTypeAttributeId: number;
|
||||
objectTypeAttribute?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
objectAttributeValues: Array<{ // Union type of value representations
|
||||
value?: string; // For scalar values (text, number, etc.)
|
||||
displayValue?: string; // Human-readable value
|
||||
referencedObject?: { // For reference attributes
|
||||
id: number;
|
||||
objectKey: string;
|
||||
label: string;
|
||||
// ⚠️ CRITICAL: referencedObject may include attributes (level 2)
|
||||
attributes?: JiraAssetsAttribute[]; // Recursive structure
|
||||
};
|
||||
status?: { // For status attributes
|
||||
name: string;
|
||||
};
|
||||
// ... other type-specific fields
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Insights
|
||||
|
||||
#### 1. Recursive Structure
|
||||
|
||||
**Issue:** `referencedObject` may include `attributes[]` array (level 2 recursion).
|
||||
|
||||
**Current Handling:**
|
||||
- `jiraAssetsClient.ts` uses `includeAttributesDeep=2` parameter
|
||||
- This causes referenced objects to include their attributes
|
||||
- Referenced objects' attributes may themselves contain referenced objects (level 3, 4, etc.)
|
||||
- **Cycles are possible** (Object A references Object B, Object B references Object A)
|
||||
|
||||
**Current Code Location:**
|
||||
- `backend/src/services/jiraAssetsClient.ts:222` - `includeAttributesDeep=2`
|
||||
- `backend/src/services/jiraAssetsClient.ts:259-260` - Search with deep attributes
|
||||
- `backend/src/services/jiraAssetsClient.ts:285-286` - POST search with deep attributes
|
||||
|
||||
**Impact:**
|
||||
- Response payloads can be very large (deeply nested)
|
||||
- Memory usage increases with depth
|
||||
- Parsing becomes more complex
|
||||
|
||||
#### 2. Shallow Referenced Objects
|
||||
|
||||
**Issue:** When `attributes[]` is absent on a shallow `referencedObject`, **do not wipe attributes**.
|
||||
|
||||
**Current Behavior:**
|
||||
- Some code paths may clear attributes if `attributes` is missing
|
||||
- This is incorrect - absence of `attributes` array does not mean "no attributes"
|
||||
- It simply means "attributes not included in this response"
|
||||
|
||||
**Critical Rule:**
|
||||
```typescript
|
||||
// ❌ WRONG: Don't do this
|
||||
if (!referencedObject.attributes) {
|
||||
referencedObject.attributes = []; // This wipes existing attributes!
|
||||
}
|
||||
|
||||
// ✅ CORRECT: Preserve existing attributes if missing from response
|
||||
if (referencedObject.attributes === undefined) {
|
||||
// Don't modify - attributes simply not included in this response
|
||||
// Keep any existing attributes that were previously loaded
|
||||
}
|
||||
```
|
||||
|
||||
**Code Locations to Review:**
|
||||
- `backend/src/services/jiraAssetsClient.ts:parseObject()` - Object parsing
|
||||
- `backend/src/services/jiraAssetsClient.ts:parseAttributeValue()` - Reference parsing
|
||||
- `backend/src/services/normalizedCacheStore.ts:loadAttributeValues()` - Reference reconstruction
|
||||
|
||||
#### 3. Attribute Values Union Type
|
||||
|
||||
**Issue:** `objectAttributeValues` is a union type - different value representations based on attribute type.
|
||||
|
||||
**Value Types:**
|
||||
- Scalar (text, number, boolean): `{ value?: string, displayValue?: string }`
|
||||
- Reference: `{ referencedObject?: { id, objectKey, label, attributes? } }`
|
||||
- Status: `{ status?: { name: string } }`
|
||||
- Date/Datetime: `{ value?: string }` (ISO string)
|
||||
|
||||
**Current Handling:**
|
||||
- `jiraAssetsClient.ts:parseAttributeValue()` uses switch on `attrDef.type`
|
||||
- Different parsing logic for each type
|
||||
- Reference types extract `referencedObject`, others use `value` or `displayValue`
|
||||
|
||||
**Code Location:**
|
||||
- `backend/src/services/jiraAssetsClient.ts:521-628` - `parseAttributeValue()` method
|
||||
|
||||
#### 4. Cycles and Recursion Depth
|
||||
|
||||
**Issue:** Recursive references can create cycles.
|
||||
|
||||
**Examples:**
|
||||
- Application A references Team X
|
||||
- Team X references Application A (via some attribute)
|
||||
- This creates a cycle at depth 2
|
||||
|
||||
**Current Handling:**
|
||||
- No explicit cycle detection
|
||||
- `includeAttributesDeep=2` limits depth but doesn't prevent cycles
|
||||
- Potential for infinite loops during reconstruction
|
||||
|
||||
**Recommendation:**
|
||||
- Add cycle detection during object reconstruction
|
||||
- Use visited set to track processed object IDs
|
||||
- Limit recursion depth explicitly (not just via API parameter)
|
||||
|
||||
**Code Locations:**
|
||||
- `backend/src/services/normalizedCacheStore.ts:loadAttributeValues()` - Reference resolution
|
||||
- `backend/src/services/normalizedCacheStore.ts:reconstructObject()` - Object reconstruction
|
||||
|
||||
### Refactoring Considerations
|
||||
|
||||
1. **Create dedicated parser module** for handling recursive payloads
|
||||
2. **Add cycle detection** utility
|
||||
3. **Separate shallow vs deep parsing** logic
|
||||
4. **Preserve attribute state** when attributes array is absent
|
||||
5. **Document recursion depth limits** clearly
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Module Dependency Graph
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ API Routes │
|
||||
│ (applications.ts, objects.ts, cache.ts, schema.ts) │
|
||||
└────────────┬────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Application Services │
|
||||
│ (cmdbService.ts, dataService.ts) │
|
||||
└────────────┬────────────────────────────────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
▼ ▼
|
||||
┌────────────┐ ┌──────────────────────────────┐
|
||||
│ Sync │ │ Normalized Cache Store │
|
||||
│ Engine │ │ (EAV Pattern) │
|
||||
└─────┬──────┘ └──────────┬───────────────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌─────────────────┐
|
||||
│ │ Query Builder │
|
||||
│ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Jira Assets Client │
|
||||
│ (jiraAssetsClient.ts, jiraAssets.ts) │
|
||||
└────────────┬────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Schema Services │
|
||||
│ (schemaDiscovery, schemaCache, etc.) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps (Phase 2)
|
||||
|
||||
1. Review and approve this plan
|
||||
2. Create detailed task breakdown for each migration step
|
||||
3. Set up feature branch for refactoring
|
||||
4. Implement changes incrementally with tests
|
||||
5. Update documentation as we go
|
||||
|
||||
---
|
||||
|
||||
**End of Phase 1 - Analysis Document**
|
||||
Reference in New Issue
Block a user