chore: remove IDE artifacts and update gitignore

- Remove .cursor/ folder from tracking (plans, worktrees, rules)
- Remove sim-api-test-log.csv from tracking
- Add .cursor/ and *.csv to gitignore
This commit is contained in:
barsa 2026-02-03 18:35:15 +09:00
parent b957877c6f
commit 70f65cedaa
6 changed files with 1 additions and 754 deletions

View File

@ -1,350 +0,0 @@
<!-- 67f8fea5-b6cb-4187-8097-25ccb37e1dcf fa268fdd-dd67-4003-bb94-8236ed95ab44 -->
# Domain & BFF Clean Architecture Refactoring
## Overview
Establish clean separation between domain (business logic) and BFF (infrastructure) layers by:
1. Removing redundant mapper service wrappers in BFF
2. Moving query builders from domain to BFF integration
3. Using domain mappers directly in BFF services
4. Eliminating unnecessary transformation layers
## Current Issues
### 1. Query Builders in Wrong Layer
**Location**: `packages/domain/orders/providers/salesforce/query.ts`
- `buildOrderSelectFields()`, `buildOrderItemSelectFields()`, `buildOrderItemProduct2Fields()`
- These are SOQL infrastructure concerns, not business logic
- Domain should not know about Salesforce query language
### 2. Redundant Mapper Service Wrapper
**Location**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts`
- Just wraps `Providers.Whmcs.mapFulfillmentOrderItems()` from domain
- Adds logging but no transformation logic
- Creates confusion about where mapping lives
### 3. Double Transformation Pattern
**Current Flow**:
```
BFF query → Raw SF data → Domain mapper → Domain type → BFF mapper??? → Same type
```
**Should Be**:
```
BFF query → Raw SF data → Domain mapper → Domain type → Use directly
```
### 4. Catalog Services Do It Correctly
**Good Example**: `apps/bff/src/modules/catalog/services/sim-catalog.service.ts`
```typescript
const product = CatalogProviders.Salesforce.mapSimProduct(record, entry);
// Uses domain mapper directly, no BFF wrapper!
```
## Architecture Principles
### Domain Layer (`packages/domain/`)
**Contains**:
- ✅ Business types (OrderDetails, OrderSummary)
- ✅ Raw provider types (SalesforceOrderRecord)
- ✅ Validation schemas (Zod)
- ✅ Transformation mappers (Raw → Domain)
- ✅ Business validation functions
**Does NOT Contain**:
- ❌ Query builders (SOQL, GraphQL)
- ❌ Field configuration
- ❌ HTTP/API concerns
### BFF Integration Layer (`apps/bff/src/integrations/`)
**Contains**:
- ✅ Query builders (SOQL construction)
- ✅ Connection services
- ✅ Integration services that:
- Build queries
- Execute queries
- Use domain mappers
- Return domain types
**Does NOT Contain**:
- ❌ Additional mapping logic
- ❌ Business validation
### BFF Application Layer (`apps/bff/src/modules/`)
**Contains**:
- ✅ Orchestrators (workflow coordination)
- ✅ Controllers (HTTP endpoints)
- ✅ Uses integration services
- ✅ Uses domain types directly
**Does NOT Contain**:
- ❌ Direct Salesforce queries
- ❌ Mapper service wrappers
- ❌ Double transformations
## Refactoring Steps
### Phase 1: Create Salesforce Integration Services
#### 1.1 Create `SalesforceOrderService`
**File**: `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts`
Encapsulates all Salesforce order operations:
- `getOrderById(orderId): Promise<OrderDetails | null>`
- `getOrdersForAccount(accountId): Promise<OrderSummary[]>`
- Builds queries internally
- Uses domain mappers for transformation
- Returns domain types
**Benefits**:
- Encapsulation of SF-specific logic
- Easy to test
- Easy to swap providers
- No SF details leak to application layer
#### 1.2 Update OrderOrchestrator
- Remove direct `this.sf.query()` calls
- Inject and use `SalesforceOrderService`
- Remove query building logic
- Just coordinate workflows
### Phase 2: Move Query Builders
#### 2.1 Move Query Builders to BFF
**From**: `packages/domain/orders/providers/salesforce/query.ts`
**To**: `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts`
Move these functions:
- `buildOrderSelectFields()`
- `buildOrderItemSelectFields()`
- `buildOrderItemProduct2Fields()`
#### 2.2 Clean Domain Exports
Remove query builder exports from:
- `packages/domain/orders/providers/salesforce/index.ts`
- `packages/domain/orders/providers/index.ts`
- `packages/domain/orders/index.ts`
### Phase 3: Remove Redundant Mapper Services
#### 3.1 Delete OrderWhmcsMapper Service
**File to DELETE**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts`
It only wraps domain mappers - provides no value.
#### 3.2 Update OrderFulfillmentOrchestrator
Replace:
```typescript
constructor(private orderWhmcsMapper: OrderWhmcsMapper) {}
const result = this.orderWhmcsMapper.mapOrderItemsToWhmcs(items);
```
With:
```typescript
import { Providers } from "@customer-portal/domain/orders";
const result = Providers.Whmcs.mapFulfillmentOrderItems(items);
```
**Direct domain mapper usage** - single transformation!
#### 3.3 Update orders.module.ts
- Remove `OrderWhmcsMapper` from providers
- Remove import statement
### Phase 4: Verify Catalog Pattern Consistency
Catalog services already follow the clean pattern - verify they continue to:
- Build queries in BFF service layer
- Use `CatalogProviders.Salesforce.mapXXXProduct()` directly
- Return domain types without additional mapping
### Phase 5: Clean Up Field Configuration (If Unused)
**Investigation needed**: Check if `SalesforceFieldConfigService` is actually used.
If NOT used in order/catalog flows:
- Consider removing or documenting as future feature
- Field names are already defined in raw types
### Phase 6: Documentation Updates
#### 6.1 Update Domain README
- Clarify that query builders don't belong in domain
- Add architecture diagram showing clear boundaries
#### 6.2 Create Integration Layer Guide
Document:
- When to create integration services
- Pattern: Query → Transform → Return domain type
- No additional mapping in BFF
#### 6.3 Update ORDERS-ARCHITECTURE-REVIEW.md
- Mark query builders as moved
- Mark mapper wrappers as removed
- Show final clean architecture
## File Changes Summary
### Files to CREATE
1. `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts`
2. `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts`
### Files to MODIFY
3. `apps/bff/src/modules/orders/services/order-orchestrator.service.ts` - Use SalesforceOrderService
4. `apps/bff/src/modules/orders/services/order-fulfillment-orchestrator.service.ts` - Use domain mapper directly
5. `apps/bff/src/modules/orders/orders.module.ts` - Update providers
6. `apps/bff/src/integrations/salesforce/salesforce.module.ts` - Export new service
7. `packages/domain/orders/providers/salesforce/index.ts` - Remove query exports
8. `packages/domain/orders/providers/index.ts` - Remove query exports
9. `packages/domain/orders/index.ts` - Remove query exports
### Files to DELETE
10. `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts`
11. `packages/domain/orders/providers/salesforce/query.ts`
### Documentation to UPDATE
12. `packages/domain/README.md`
13. `ORDERS-ARCHITECTURE-REVIEW.md`
14. Create: `docs/BFF-INTEGRATION-PATTERNS.md`
## Expected Outcomes
### Architecture Cleanliness
- ✅ Single source of truth for transformations (domain mappers)
- ✅ Clear separation: domain = business, BFF = infrastructure
- ✅ No redundant mapping layers
- ✅ Query logic in correct layer (BFF integration)
### Code Quality
- ✅ Easier to test (clear boundaries)
- ✅ Easier to maintain (no duplication)
- ✅ Easier to understand (one transformation path)
- ✅ Easier to swap providers (integration services encapsulate)
### Developer Experience
- ✅ Clear patterns to follow
- ✅ No confusion about where code goes
- ✅ Consistent with catalog services
- ✅ Self-documenting architecture
## Migration Notes
### Breaking Changes
**None for consumers** - All changes are internal refactoring
### Testing
- Unit test new `SalesforceOrderService`
- Verify order creation flow still works
- Verify order fulfillment flow still works
- Verify catalog fetching still works
### Rollback Plan
Git history preserves old structure - can revert commits if issues arise.
## Success Criteria
- [x] Query builders moved to BFF integration layer
- [x] `OrderWhmcsMapper` service deleted
- [x] `SalesforceOrderService` created and used
- [x] `OrderOrchestrator` no longer builds SOQL queries
- [x] `OrderFulfillmentOrchestrator` uses domain mapper directly
- [x] Domain exports cleaned (no query builders)
- [x] Documentation updated
- [x] All tests passing (no linting errors)
- [x] Order creation works end-to-end (ready for testing)
- [x] Order fulfillment works end-to-end (ready for testing)
---
**Final Architecture**:
```
┌─────────────────────────────────────────┐
│ Controller (HTTP) │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Orchestrator (Application) │
│ - Coordinates workflows │
│ - Uses Integration Services │
│ - Works with Domain Types │
└──────────────┬──────────────────────────┘
┌──────────┴──────────┐
│ │
┌───▼───────────┐ ┌──────▼──────────────┐
│ Domain │ │ Integration │
│ (Business) │ │ (Infrastructure) │
│ │ │ │
│ • Types │ │ • SF OrderService │
│ • Schemas │ │ • Query Builders │
│ • Mappers ────┼──┤ • Connections │
│ • Validators │ │ • Field Mapping │
└───────────────┘ └─────────────────────┘
Flow: Query (BFF) → Raw Data → Domain Mapper → Domain Type → Use Directly
└─ One transformation, no duplication ─┘
```
### To-dos
- [x] Create SalesforceOrderService in BFF integration layer with methods: getOrderById, getOrdersForAccount
- [x] Move query builders from packages/domain/orders/providers/salesforce/query.ts to apps/bff/src/integrations/salesforce/utils/order-query-builder.ts
- [x] Update OrderOrchestrator to use SalesforceOrderService instead of direct SF queries
- [x] Delete redundant OrderWhmcsMapper service wrapper
- [x] Update OrderFulfillmentOrchestrator to use domain Providers.Whmcs mapper directly
- [x] Remove query builder exports from domain package index files
- [x] Update orders.module.ts and salesforce.module.ts with new services
- [x] Verify catalog services follow same clean pattern (already correct)
- [x] Update domain README and architecture documentation with clean patterns
- [x] Test order creation and fulfillment flows end-to-end

View File

@ -1,390 +0,0 @@
---
name: Restructure to Account Portal
overview: Restructure the app to have public pages under (public)/ and all authenticated portal pages under /account/*, with auth-aware headers in public shells.
todos:
- id: auth-aware-public-shell
content: "Make PublicShell auth-aware: show 'My Account' for logged-in users, 'Sign in' for guests"
status: pending
- id: auth-aware-catalog-shell
content: Make CatalogShell auth-aware with same pattern
status: pending
- id: create-account-layout
content: Create account/layout.tsx with AppShell and auth guard redirect
status: pending
- id: move-dashboard-to-account
content: Move dashboard page to account/page.tsx
status: pending
- id: move-billing-to-account
content: Move billing pages to account/billing/*
status: pending
- id: move-subscriptions-to-services
content: Move subscriptions to account/services/*
status: pending
- id: move-orders-to-account
content: Move orders to account/orders/*
status: pending
- id: move-support-to-account
content: Move support cases to account/support/*
status: pending
- id: move-profile-to-settings
content: Move account/profile to account/settings/*
status: pending
- id: fix-shop-double-header
content: Fix shop layout to not create double header - add CatalogNav only
status: pending
- id: create-contact-route
content: Create (public)/contact/page.tsx for contact form
status: pending
- id: update-navigation
content: Update AppShell navigation.ts with /account/* paths
status: pending
- id: update-catalog-links
content: Replace all /catalog links with /shop
status: pending
- id: update-portal-links
content: Replace all old portal links with /account/* paths
status: pending
- id: remove-sfnumber
content: Remove sfNumber from domain schema and signup components
status: pending
- id: delete-old-authenticated
content: Delete (authenticated)/ directory after migration
status: pending
- id: rebuild-test
content: Rebuild domain package and test all routes
status: pending
---
# Restructure Portal to /account/\* Architecture
## Target Architecture
```mermaid
flowchart TB
subgraph public ["(public)/ - Public Pages"]
P1["/"] --> Home["Homepage"]
P2["/auth/*"] --> Auth["Login, Signup, etc"]
P3["/shop/*"] --> Shop["Product Catalog"]
P4["/help"] --> Help["FAQ & Knowledge Base"]
P5["/contact"] --> Contact["Contact Form"]
P6["/order/*"] --> Order["Checkout Flow"]
end
subgraph account ["/account/* - My Portal"]
A1["/account"] --> Dashboard["Dashboard"]
A2["/account/billing"] --> Billing["Invoices & Payments"]
A3["/account/services"] --> Services["My Subscriptions"]
A4["/account/orders"] --> Orders["Order History"]
A5["/account/support"] --> Support["My Tickets"]
A6["/account/settings"] --> Settings["Profile Settings"]
end
public -.->|"Auth-aware header"| account
```
---
## Phase 1: Make Shells Auth-Aware
### 1.1 Update PublicShell
**File:** `apps/portal/src/components/templates/PublicShell/PublicShell.tsx`
Add auth detection to header navigation:
```tsx
"use client";
import { useAuthStore } from "@/features/auth/services/auth.store";
export function PublicShell({ children }: PublicShellProps) {
const { isAuthenticated } = useAuthStore();
return (
<div className="min-h-screen...">
<header>
<nav>
<Link href="/shop">Services</Link>
<Link href="/help">Support</Link>
{isAuthenticated ? (
<Link href="/account" className="primary-button">
My Account
</Link>
) : (
<Link href="/auth/login" className="primary-button">
Sign in
</Link>
)}
</nav>
</header>
...
</div>
);
}
```
### 1.2 Update CatalogShell
**File:** `apps/portal/src/components/templates/CatalogShell/CatalogShell.tsx`
Same auth-aware pattern - show "My Account" or "Sign in" based on auth state.
---
## Phase 2: Create /account Route Structure
### 2.1 Create Account Layout with Auth Guard
**File:** `apps/portal/src/app/account/layout.tsx` (NEW)
```tsx
import { redirect } from "next/navigation";
import { cookies } from "next/headers";
import { AppShell } from "@/components/organisms/AppShell";
export default async function AccountLayout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies();
const hasAuthToken = cookieStore.has("access_token");
if (!hasAuthToken) {
redirect("/auth/login?redirect=/account");
}
return <AppShell>{children}</AppShell>;
}
```
### 2.2 Create Account Pages
Move and rename pages:
| Current Path | New Path | New File |
|--------------|----------|----------|
| `(authenticated)/dashboard/page.tsx` | `/account` | `account/page.tsx` |
| `(authenticated)/billing/*` | `/account/billing/*` | `account/billing/*` |
| `(authenticated)/subscriptions/*` | `/account/services/*` | `account/services/*` |
| `(authenticated)/orders/*` | `/account/orders/*` | `account/orders/*` |
| `(authenticated)/support/*` | `/account/support/*` | `account/support/*` |
| `(authenticated)/account/*` | `/account/settings/*` | `account/settings/*` |
---
## Phase 3: Update Navigation
### 3.1 Update AppShell Navigation
**File:** `apps/portal/src/components/organisms/AppShell/navigation.ts`
Update all paths to use `/account/*`:
```typescript
export const baseNavigation: NavigationItem[] = [
{ name: "Dashboard", href: "/account", icon: HomeIcon },
{ name: "Orders", href: "/account/orders", icon: ClipboardDocumentListIcon },
{
name: "Billing",
icon: CreditCardIcon,
children: [
{ name: "Invoices", href: "/account/billing/invoices" },
{ name: "Payment Methods", href: "/account/billing/payments" },
],
},
{
name: "My Services",
icon: ServerIcon,
children: [{ name: "All Services", href: "/account/services" }],
},
{ name: "Shop", href: "/shop", icon: Squares2X2Icon }, // Links to public shop
{
name: "Support",
icon: ChatBubbleLeftRightIcon,
children: [
{ name: "My Tickets", href: "/account/support" },
{ name: "New Ticket", href: "/account/support/new" },
],
},
{ name: "Settings", href: "/account/settings", icon: UserIcon },
{ name: "Log out", href: "#", icon: ArrowRightStartOnRectangleIcon, isLogout: true },
];
```
---
## Phase 4: Fix Public Routes
### 4.1 Fix Double Header in Shop
Remove the nested shell issue by having CatalogShell NOT render a full page wrapper, or by not nesting it under PublicShell.
**Option A:** Move shop out of (public) to its own route with CatalogShell only
**Option B:** Have (public)/shop/layout.tsx return just children with catalog nav (no shell)
Recommended: **Option B** - Keep shop under (public) but have shop layout add only catalog navigation, not a full shell.
**File:** `apps/portal/src/app/(public)/shop/layout.tsx`
```tsx
import { CatalogNav } from "@/components/templates/CatalogShell";
export default function ShopLayout({ children }: { children: React.ReactNode }) {
// Don't wrap with another shell - parent (public) layout already has PublicShell
return (
<>
<CatalogNav />
{children}
</>
);
}
```
**File:** `apps/portal/src/components/templates/CatalogShell/CatalogShell.tsx`
Split into two exports:
- `CatalogShell` - full shell (if ever needed standalone)
- `CatalogNav` - just the navigation bar
### 4.2 Create /contact Route
**File:** `apps/portal/src/app/(public)/contact/page.tsx` (NEW)
Move content from `(public)/help/contact/` to `(public)/contact/`.
---
## Phase 5: Delete Old Routes
### 5.1 Delete (authenticated) Directory
After moving all content to /account/:
- Delete entire `apps/portal/src/app/(authenticated)/` directory
### 5.2 Clean Up Unused Files
- Delete `(public)/help/contact/` (moved to /contact)
- Keep `(public)/help/page.tsx` for FAQ
---
## Phase 6: Update All Internal Links
### 6.1 Update /catalog to /shop Links
Replace in feature components (11 files, 27 occurrences):
```
/catalog → /shop
/catalog/internet → /shop/internet
/catalog/sim → /shop/sim
/catalog/vpn → /shop/vpn
```
### 6.2 Update Dashboard/Portal Links
Replace throughout codebase:
```
/dashboard → /account
/billing → /account/billing
/subscriptions → /account/services
/orders → /account/orders
/support/cases → /account/support
```
---
## Phase 7: Remove sfNumber from Signup
### 7.1 Update Domain Schema
**File:** `packages/domain/auth/schema.ts`
```typescript
// Line 44: Remove required sfNumber
// Before:
sfNumber: z.string().min(6, "Customer number must be at least 6 characters"),
// After:
sfNumber: z.string().optional(),
```
Also update `validateSignupRequestSchema` to not require sfNumber.
### 7.2 Update SignupForm Components
- `SignupForm.tsx` - Remove sfNumber from initialValues and validation
- `AccountStep.tsx` - Remove Customer Number form field
- `ReviewStep.tsx` - Remove Customer Number display
---
## Phase 8: Rebuild and Test
### 8.1 Rebuild Domain Package
```bash
pnpm --filter @customer-portal/domain build
```
### 8.2 Test Matrix
| Scenario | URL | Expected |
|----------|-----|----------|
| Public homepage | `/` | PublicShell, homepage content |
| Public shop | `/shop` | CatalogShell (auth-aware), products |
| Auth user in shop | `/shop` | "My Account" button, personalized pricing |
| Public help | `/help` | FAQ content |
| Public contact | `/contact` | Contact form, prefills if logged in |
| Login | `/auth/login` | Login form |
| Signup | `/auth/signup` | No sfNumber field |
| Account dashboard | `/account` | AppShell, dashboard (redirect if not auth) |
| My services | `/account/services` | Subscriptions list |
| My tickets | `/account/support` | Support cases |
| Checkout | `/order` | CheckoutShell, wizard |
---
## Files Summary
| Category | Action | Count |
|----------|--------|-------|
| New account/ routes | Create | ~15 files |
| Shell components | Modify | 2 (PublicShell, CatalogShell) |
| Shop layout | Modify | 1 |
| Navigation | Modify | 1 |
| Link updates | Modify | ~20 files |
| Domain schema | Modify | 1 |
| Signup components | Modify | 3 |
| Delete old routes | Delete | ~20 files |
**Total: ~60+ file operations**

View File

@ -1,10 +0,0 @@
---
alwaysApply: true
---
## Codebase Coding Standard
1. Have types and validation in the shared domain layer.
2. Keep business logic out of the frontend; use services and APIs instead.
3. Reuse existing types and functions; extend them when additional behavior is needed.
4. Read and understand the structures and workflows documented in docs. Start from docs/README.md
5. Follow structures and our codebase rules inside docs/development directory

View File

@ -1,3 +0,0 @@
{
"setup-worktree": ["pnpm install"]
}

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ build/
# IDE and editor files # IDE and editor files
.vscode/ .vscode/
.idea/ .idea/
.cursor/
*.swp *.swp
*.swo *.swo
*~ *~

View File

@ -1 +0,0 @@
Timestamp,API Endpoint,API Method,Phone Number,SIM Identifier,Request Payload,Response Status,Error,Additional Info
1 Timestamp API Endpoint API Method Phone Number SIM Identifier Request Payload Response Status Error Additional Info