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
153 lines
12 KiB
Markdown
153 lines
12 KiB
Markdown
# 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:
|
|
1. Defines shared portal type definitions
|
|
2. Re-exports portal actions to break direct dependencies
|
|
3. 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 package
|
|
- `package.json` - Package configuration
|
|
- `tsconfig.json` - TypeScript configuration
|
|
- `README.md` - Package documentation
|
|
- `src/types/index.ts` - Shared type definitions
|
|
- `src/actions/index.ts` - Actions index
|
|
- `src/actions/portalInvitationActions.ts` - Re-exported actions
|
|
- `src/index.ts` - Package entry point
|
|
|
|
### Updated Components
|
|
- **Modified:** `packages/clients/src/components/contacts/ContactPortalTab.tsx`
|
|
- Updated imports to use `@alga-psa/portal-shared` instead of `@alga-psa/client-portal`
|
|
|
|
- **Modified:** `packages/clients/src/components/contacts/ContactAvatarUpload.tsx`
|
|
- Updated imports to use `@alga-psa/portal-shared` instead of `@alga-psa/client-portal`
|
|
|
|
## Dependency Flow
|
|
|
|
### Before (Violation)
|
|
```typescript
|
|
// packages/clients/src/components/contacts/ContactPortalTab.tsx
|
|
import { sendPortalInvitation } from '@alga-psa/client-portal/actions'; // ❌ Direct cross-domain import
|
|
```
|
|
|
|
### After (Fixed)
|
|
```typescript
|
|
// 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:
|
|
|
|
1. **No Circular Dependencies** - `@alga-psa/clients` does not directly depend on `@alga-psa/client-portal`
|
|
2. **Clean Layering** - Infrastructure layer (portal-shared) bridges domain-level concerns
|
|
3. **Separation of Concerns** - Each domain package owns its business logic
|
|
4. **Dependency Inversion** - Domain packages depend on abstractions (infrastructure re-exports)
|
|
5. **Facade Pattern** - Infrastructure layer provides a simplified interface to domain functionality
|
|
|
|
## Future Improvements
|
|
|
|
If portal functionality continues to grow, consider:
|
|
|
|
1. **Moving implementations to portal-shared** - Move actual `PortalInvitationService` and action implementations
|
|
2. **Creating domain event bus** - For more complex cross-domain communication patterns
|
|
3. **Using dependency injection** - For more sophisticated dependency management
|
|
4. **Creating orchestration layer** - Between application layer and domain packages for complex workflows
|