Phase 5: Integration, Shared Infrastructure & Testing
Overview
Now that all three vertical slices are built, it's time to integrate them into a cohesive application. This phase focuses on testing integration points, building shared infrastructure, and ensuring all event-driven communication works seamlessly.
Learning Objectives
- Test event-driven integration between features
- Build shared component library
- Implement unified auth and data layer
- Write comprehensive integration tests
- Resolve integration issues collaboratively
Requirements
1. Integration Testing - Event Flow
Test Complete Workflows:
Create src/__tests__/integration/shipment-workflow.test.ts:
typescript1import { eventBus } from '@/services/event-bus';2import { EventType } from '@/shared/types/events';34describe('Complete Shipment Workflow', () => {5 beforeEach(() => {6 // Reset event bus and clear storage7 jest.clearAllMocks();8 });910 it('should create shipment and trigger tracking initialization', async () => {11 const trackingEvents: any[] = [];1213 // Subscribe to SHIPMENT_CREATED (Person 2 simulation)14 eventBus.subscribe(EventType.SHIPMENT_CREATED, async (event) => {15 trackingEvents.push(event);16 });1718 // Create shipment (Person 1)19 const shipment = await createShipment(mockShipmentData);2021 // Wait for event propagation22 await new Promise(resolve => setTimeout(resolve, 100));2324 // Verify event was emitted25 expect(trackingEvents).toHaveLength(1);26 expect(trackingEvents[0].payload.shipment.id).toBe(shipment.id);27 });2829 it('should propagate status changes to notifications', async () => {30 const notifications: any[] = [];3132 // Subscribe to STATUS_CHANGED (Person 3 simulation)33 eventBus.subscribe(EventType.SHIPMENT_STATUS_CHANGED, async (event) => {34 notifications.push(event);35 });3637 // Update status (Person 2)38 await updateShipmentStatus('ship-1', ShipmentStatus.DELIVERED);3940 await new Promise(resolve => setTimeout(resolve, 100));4142 // Verify notification triggered43 expect(notifications).toHaveLength(1);44 expect(notifications[0].payload.newStatus).toBe(ShipmentStatus.DELIVERED);45 });4647 it('should support reorder workflow', async () => {48 // Person 1: Create initial shipment49 const originalShipment = await createShipment(mockShipmentData);5051 // Person 3: Reorder using facade52 const facade = new OrderManagementFacade();53 const newOrder = await facade.reorder('user-1', originalShipment.id);5455 // Verify new shipment created with same details56 expect(newOrder.orderId).not.toBe(originalShipment.orderId);57 expect(newOrder.cost).toBe(originalShipment.cost);58 });59});
2. Shared Component Library
Build Common UI Components:
Create src/shared/components/ui/ with:
typescript1// Button.tsx2export function Button({ variant, size, children, ...props }: ButtonProps) {3 return (4 <button5 className={cn(6 'rounded-lg font-medium transition-colors',7 variantStyles[variant],8 sizeStyles[size]9 )}10 {...props}11 >12 {children}13 </button>14 );15}1617// Card.tsx18export function Card({ title, children }: CardProps) {19 return (20 <div className="bg-white rounded-lg shadow-md p-6">21 {title && <h3 className="text-lg font-semibold mb-4">{title}</h3>}22 {children}23 </div>24 );25}2627// Input.tsx28export function Input({ label, error, ...props }: InputProps) {29 return (30 <div>31 {label && <label className="block text-sm font-medium mb-1">{label}</label>}32 <input33 className={cn(34 'w-full rounded-md border px-3 py-2',35 error ? 'border-red-500' : 'border-gray-300'36 )}37 {...props}38 />39 {error && <p className="text-sm text-red-500 mt-1">{error}</p>}40 </div>41 );42}4344// Badge.tsx45export function Badge({ status }: { status: ShipmentStatus }) {46 const styles = {47 [ShipmentStatus.CREATED]: 'bg-gray-100 text-gray-800',48 [ShipmentStatus.PICKED_UP]: 'bg-blue-100 text-blue-800',49 [ShipmentStatus.IN_TRANSIT]: 'bg-yellow-100 text-yellow-800',50 [ShipmentStatus.OUT_FOR_DELIVERY]: 'bg-orange-100 text-orange-800',51 [ShipmentStatus.DELIVERED]: 'bg-green-100 text-green-800',52 [ShipmentStatus.EXCEPTION]: 'bg-red-100 text-red-800',53 };5455 return (56 <span className={cn('px-2 py-1 rounded-full text-xs font-medium', styles[status])}>57 {status}58 </span>59 );60}6162// LoadingSpinner.tsx63export function LoadingSpinner({ size = 'md' }: { size?: 'sm' | 'md' | 'lg' }) {64 return (65 <div className={cn('animate-spin rounded-full border-b-2', sizeClasses[size])} />66 );67}
3. Unified Authentication
Create src/shared/services/auth-service.ts:
typescript1export interface User {2 id: string;3 email: string;4 name: string;5 role: 'customer' | 'admin' | 'driver';6}78class AuthService {9 private currentUser: User | null = null;1011 async login(email: string, password: string): Promise<User> {12 // Mock authentication13 const user: User = {14 id: crypto.randomUUID(),15 email,16 name: 'Test User',17 role: 'customer',18 };1920 this.currentUser = user;21 localStorage.setItem('user', JSON.stringify(user));2223 return user;24 }2526 async logout(): Promise<void> {27 this.currentUser = null;28 localStorage.removeItem('user');29 }3031 getCurrentUser(): User | null {32 if (this.currentUser) {33 return this.currentUser;34 }3536 const stored = localStorage.getItem('user');37 if (stored) {38 this.currentUser = JSON.parse(stored);39 }4041 return this.currentUser;42 }4344 isAuthenticated(): boolean {45 return this.getCurrentUser() !== null;46 }4748 hasRole(role: User['role']): boolean {49 const user = this.getCurrentUser();50 return user?.role === role;51 }52}5354export const authService = new AuthService();
Create src/shared/components/ProtectedRoute.tsx:
typescript1'use client'23export function ProtectedRoute({ children, requireRole }: ProtectedRouteProps) {4 const user = authService.getCurrentUser();56 if (!user) {7 return <Navigate to="/login" />;8 }910 if (requireRole && user.role !== requireRole) {11 return <div>Unauthorized</div>;12 }1314 return <>{children}</>;15}
4. Unified Data Layer
Create src/shared/services/storage-service.ts:
typescript1class StorageService {2 private readonly SHIPMENTS_KEY = 'shipments';3 private readonly TRACKING_KEY = 'tracking_events';4 private readonly NOTIFICATIONS_KEY = 'notifications';56 // Shipments (Person 1)7 async saveShipment(shipment: Shipment): Promise<void> {8 const shipments = this.getShipments();9 const index = shipments.findIndex(s => s.id === shipment.id);1011 if (index >= 0) {12 shipments[index] = shipment;13 } else {14 shipments.push(shipment);15 }1617 localStorage.setItem(this.SHIPMENTS_KEY, JSON.stringify(shipments));18 }1920 getShipments(): Shipment[] {21 const data = localStorage.getItem(this.SHIPMENTS_KEY);22 return data ? JSON.parse(data) : [];23 }2425 getShipment(id: string): Shipment | null {26 const shipments = this.getShipments();27 return shipments.find(s => s.id === id) || null;28 }2930 // Tracking Events (Person 2)31 async saveTrackingEvent(event: TrackingEvent): Promise<void> {32 const events = this.getTrackingEvents();33 events.push(event);34 localStorage.setItem(this.TRACKING_KEY, JSON.stringify(events));35 }3637 getTrackingEvents(shipmentId?: string): TrackingEvent[] {38 const data = localStorage.getItem(this.TRACKING_KEY);39 const events: TrackingEvent[] = data ? JSON.parse(data) : [];4041 return shipmentId42 ? events.filter(e => e.shipmentId === shipmentId)43 : events;44 }4546 // Notifications (Person 3)47 async saveNotification(notification: Notification): Promise<void> {48 const notifications = this.getNotifications();49 notifications.push(notification);50 localStorage.setItem(this.NOTIFICATIONS_KEY, JSON.stringify(notifications));51 }5253 getNotifications(userId?: string): Notification[] {54 const data = localStorage.getItem(this.NOTIFICATIONS_KEY);55 const notifications: Notification[] = data ? JSON.parse(data) : [];5657 return userId58 ? notifications.filter(n => n.userId === userId)59 : notifications;60 }6162 // Clear all data63 clearAll(): void {64 localStorage.removeItem(this.SHIPMENTS_KEY);65 localStorage.removeItem(this.TRACKING_KEY);66 localStorage.removeItem(this.NOTIFICATIONS_KEY);67 }68}6970export const storageService = new StorageService();
5. Integration Issue Resolution
Common Integration Issues:
-
Event Not Received
- Check EventBus subscription is active
- Verify event type matches exactly
- Ensure publish is awaited
-
Type Mismatches
- Use shared types from
src/shared/types/ - Never duplicate type definitions
- Run
npm run typecheckfrequently
- Use shared types from
-
Merge Conflicts
- Communicate before changing shared files
- Pull from develop frequently
- Resolve conflicts immediately
-
Data Inconsistency
- Use centralized StorageService
- Validate data at boundaries
- Test with real data scenarios
6. Code Review Process
Team Code Review Checklist:
For each PR, reviewers check:
- Follows shared type definitions
- Pattern implementation is correct
- Tests included and passing
- No duplicate code from other slices
- Event integration works correctly
- No console.logs or debugging code
- TypeScript errors resolved
- Accessibility compliant
- Mobile responsive
Review Rotation:
- Person 1's code reviewed by Person 2 & 3
- Person 2's code reviewed by Person 1 & 3
- Person 3's code reviewed by Person 1 & 2
7. End-to-End Testing
Create e2e/complete-workflow.spec.ts:
typescript1import { test, expect } from '@playwright/test';23test.describe('Complete Platform Workflow', () => {4 test('should create shipment, track status, and receive notification', async ({ page }) => {5 // Login6 await page.goto('http://localhost:3000/login');7 await page.fill('[name="email"]', 'test@example.com');8 await page.fill('[name="password"]', 'password');9 await page.click('button:text("Login")');1011 // Create shipment (Person 1's feature)12 await page.goto('/shipments/create');13 await page.fill('[name="package.weight"]', '10');14 await page.fill('[name="origin.postalCode"]', '10001');15 await page.fill('[name="destination.postalCode"]', '90001');16 await page.click('button:text("Create Shipment")');1718 // Verify tracking initialized (Person 2's feature)19 const trackingNumber = await page.locator('[data-testid="tracking-number"]').textContent();20 await page.goto(`/tracking/${trackingNumber}`);21 await expect(page.locator('text=/created/i')).toBeVisible();2223 // Check notification sent (Person 3's feature)24 await page.goto('/portal/notifications');25 await expect(page.locator('text=/shipment created/i')).toBeVisible();2627 // Verify analytics updated (Person 3's feature)28 await page.goto('/portal/analytics');29 await expect(page.locator('[data-testid="total-shipments"]')).toContainText('1');30 });31});
Deliverables
- All vertical slices integrated successfully
- Event-driven communication tested and working
- Shared component library (10+ components)
- Unified authentication system
- Centralized data layer (StorageService)
- Integration tests (20+ tests, all workflows covered)
- E2E tests (5+ critical paths)
- Code reviews completed for all features
- Test coverage 70%+ overall
- No TypeScript errors across entire codebase
Testing Requirements Summary
Unit Tests: Each person maintains 70%+ coverage for their vertical slice
Integration Tests: Team writes together
- Shipment creation → Tracking initialization
- Status update → Notification sending
- Reorder → Shipment creation
E2E Tests: Team writes together
- Complete user journey (create → track → notify)
- Exception handling workflow
- Analytics updates
Total Coverage Target: 70%+
Validation Checklist
- All event subscribers receive events correctly
- No duplicate data in storage
- Type safety maintained across boundaries
- All team members can run full test suite
- No merge conflicts in develop branch
- Shared components used consistently
- Authentication works across all features
- Mobile and desktop views work correctly
Team Coordination
Integration Sprint Schedule:
Week 1:
- Day 1-2: Integrate Person 1 & 2 (Shipment → Tracking)
- Day 3-4: Integrate Person 2 & 3 (Tracking → Notifications)
- Day 5: Integrate Person 3 & 1 (Portal → Shipments)
Week 2:
- Day 1-2: Build shared components
- Day 3-4: Write integration tests
- Day 5: Code reviews and refinements
Resources
Estimated Time
- Integration: 8 hours (team)
- Shared infrastructure: 6 hours (distributed)
- Testing: 10 hours (team)
- Code reviews: 4 hours (team)
- Total: 28 hours (distributed across team)
Next Steps
Proceed to Phase 6: Demo, Documentation & Deployment for final polish, comprehensive documentation, team demo creation, and deployment.