Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
12 KiB
12 KiB
Portal Layering Architecture Fix
Problem
The ContactPortalTab component in @alga-psa/clients was directly importing portal-related actions from @alga-psa/client-portal, creating a horizontal (cross-layer) dependency between two domain layer packages:
❌ VIOLATION:
@alga-psa/clients (Layer 3 - Domain)
↓ imports directly from ↓
@alga-psa/client-portal (Layer 3 - Domain)
Solution
Created a new infrastructure layer package @alga-psa/portal-shared (Layer 2) that:
- Defines shared portal type definitions
- Re-exports portal actions to break direct dependencies
- Allows both domain packages to safely depend on it without circular dependencies
✅ FIXED:
@alga-psa/clients (Layer 3 - Domain)
↓ imports from ↓
@alga-psa/portal-shared (Layer 2 - Infrastructure) ← NEW PACKAGE
↓ re-exports from ↓
@alga-psa/client-portal (Layer 3 - Domain)
Updated Architecture Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 5: Application │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ server (MSP app) │ EE modules │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────────────────────┘
│ (depends on all layers below)
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 4: Presentation │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ @alga-psa/ui (UI components, hooks, utilities) │ │
│ │ (No dependencies on domain logic) │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 3: Domain Services │
│ (Each represents a business domain - should NOT depend on each other) │
│ ┌──────────────┬──────────────┬──────────────┬──────────────┬────────────┐ │
│ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/│ │
│ │ clients │ tickets │ client-portal│ projects │ billing │ │
│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │
│ ┌──────────────┬──────────────┬──────────────┬──────────────┬────────────┐ │
│ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/│ │
│ │ documents │ tags │notifications │ workflows │scheduling │ │
│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │
│ │
│ ✅ NO horizontal dependencies between packages (enforced via │
│ infrastructure layer re-exports when cross-cutting logic exists) │
└──────────────────────────┬──────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 2: Infrastructure │
│ (Cross-cutting concerns and technical services) │
│ ┌──────────────┬──────────────┬──────────────┬──────────────┬────────────┐ │
│ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ │
│ │ auth │ users │ media │ email │ tenancy │ │
│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │
│ ┌──────────────┬──────────────┬──────────────┬──────────────┬────────────┐ │
│ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ │
│ │ validation │ integrations │ reference- │ portal-shared│ core │ │
│ │ │ │ data │ │ │ │
│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │
│ │
│ NEW: @alga-psa/portal-shared - Re-exports portal functionality to break │
│ domain-level cross-dependencies │
└──────────────────────────┬──────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 1: Foundation / Core │
│ (No dependencies on any other @alga-psa/* packages) │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ @alga-psa/ │ @alga-psa/ │ @alga-psa/ │ │
│ │ types │ core │ db │ │
│ │ │ │ │ │
│ │ - Interfaces │ - Utilities │ - Migrations │ │
│ │ - Type defs │ - Helpers │ - Schemas │ │
│ │ - Constants │ - Encryption │ - Knex setup │ │
│ └──────────────┴──────────────┴──────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Files Changed
New Package
- Created:
packages/portal-shared/- New infrastructure layer packagepackage.json- Package configurationtsconfig.json- TypeScript configurationREADME.md- Package documentationsrc/types/index.ts- Shared type definitionssrc/actions/index.ts- Actions indexsrc/actions/portalInvitationActions.ts- Re-exported actionssrc/index.ts- Package entry point
Updated Components
-
Modified:
packages/clients/src/components/contacts/ContactPortalTab.tsx- Updated imports to use
@alga-psa/portal-sharedinstead of@alga-psa/client-portal
- Updated imports to use
-
Modified:
packages/clients/src/components/contacts/ContactAvatarUpload.tsx- Updated imports to use
@alga-psa/portal-sharedinstead of@alga-psa/client-portal
- Updated imports to use
Dependency Flow
Before (Violation)
// packages/clients/src/components/contacts/ContactPortalTab.tsx
import { sendPortalInvitation } from '@alga-psa/client-portal/actions'; // ❌ Direct cross-domain import
After (Fixed)
// packages/clients/src/components/contacts/ContactPortalTab.tsx
import { sendPortalInvitation } from '@alga-psa/portal-shared/actions'; // ✅ Via infrastructure layer
// packages/portal-shared/src/actions/portalInvitationActions.ts
export { sendPortalInvitation } from '@alga-psa/client-portal/actions'; // Re-export pattern
Architecture Principles
This solution maintains the following architectural principles:
- No Circular Dependencies -
@alga-psa/clientsdoes not directly depend on@alga-psa/client-portal - Clean Layering - Infrastructure layer (portal-shared) bridges domain-level concerns
- Separation of Concerns - Each domain package owns its business logic
- Dependency Inversion - Domain packages depend on abstractions (infrastructure re-exports)
- Facade Pattern - Infrastructure layer provides a simplified interface to domain functionality
Future Improvements
If portal functionality continues to grow, consider:
- Moving implementations to portal-shared - Move actual
PortalInvitationServiceand action implementations - Creating domain event bus - For more complex cross-domain communication patterns
- Using dependency injection - For more sophisticated dependency management
- Creating orchestration layer - Between application layer and domain packages for complex workflows