Some checks failed
CI / Lint & Format Check (push) Failing after 1m11s
CI / Test Backend (push) Failing after 1m32s
CI / Build Backend (push) Has been skipped
Security Audit / npm audit (push) Failing after 5s
Security Audit / Dependency Review (push) Has been skipped
CI / Test Frontend (push) Failing after 29s
CI / Build Frontend (push) Has been skipped
472 lines
11 KiB
Markdown
472 lines
11 KiB
Markdown
# 🚀 Next Steps - Getting Started with Development
|
|
|
|
You've successfully completed Sprint 0! Here's what to do next.
|
|
|
|
---
|
|
|
|
## 🎯 Immediate Actions (Today)
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
# From the root directory
|
|
npm install
|
|
```
|
|
|
|
**Expected**: This will take 2-3 minutes. You may see some deprecation warnings (normal).
|
|
|
|
**On Windows**: If you see `EISDIR` symlink errors, that's okay - dependencies are still installed.
|
|
|
|
### 2. Start Docker Services
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
**Expected**: PostgreSQL and Redis containers will start.
|
|
|
|
**Verify**:
|
|
```bash
|
|
docker-compose ps
|
|
|
|
# You should see:
|
|
# xpeditis-postgres - Up (healthy)
|
|
# xpeditis-redis - Up (healthy)
|
|
```
|
|
|
|
### 3. Setup Environment Files
|
|
|
|
```bash
|
|
# Backend
|
|
cp apps/backend/.env.example apps/backend/.env
|
|
|
|
# Frontend
|
|
cp apps/frontend/.env.example apps/frontend/.env
|
|
```
|
|
|
|
**Note**: Default values work for local development. No changes needed!
|
|
|
|
### 4. Start the Backend
|
|
|
|
```bash
|
|
# Option 1: From root
|
|
npm run backend:dev
|
|
|
|
# Option 2: From backend directory
|
|
cd apps/backend
|
|
npm run dev
|
|
```
|
|
|
|
**Expected Output**:
|
|
```
|
|
╔═══════════════════════════════════════╗
|
|
║ 🚢 Xpeditis API Server Running ║
|
|
║ API: http://localhost:4000/api/v1 ║
|
|
║ Docs: http://localhost:4000/api/docs ║
|
|
╚═══════════════════════════════════════╝
|
|
```
|
|
|
|
**Verify**: Open http://localhost:4000/api/v1/health
|
|
|
|
### 5. Start the Frontend (New Terminal)
|
|
|
|
```bash
|
|
# Option 1: From root
|
|
npm run frontend:dev
|
|
|
|
# Option 2: From frontend directory
|
|
cd apps/frontend
|
|
npm run dev
|
|
```
|
|
|
|
**Expected Output**:
|
|
```
|
|
▲ Next.js 14.0.4
|
|
- Local: http://localhost:3000
|
|
✓ Ready in 2.3s
|
|
```
|
|
|
|
**Verify**: Open http://localhost:3000
|
|
|
|
---
|
|
|
|
## ✅ Verification Checklist
|
|
|
|
Before proceeding to development, verify:
|
|
|
|
- [ ] `npm install` completed successfully
|
|
- [ ] Docker containers are running (check with `docker-compose ps`)
|
|
- [ ] Backend starts without errors
|
|
- [ ] Health endpoint returns 200 OK: http://localhost:4000/api/v1/health
|
|
- [ ] Swagger docs accessible: http://localhost:4000/api/docs
|
|
- [ ] Frontend loads: http://localhost:3000
|
|
- [ ] No TypeScript compilation errors
|
|
|
|
**All green? You're ready to start Phase 1! 🎉**
|
|
|
|
---
|
|
|
|
## 📅 Phase 1 - Core Search & Carrier Integration (Next 6-8 weeks)
|
|
|
|
### Week 1-2: Domain Layer & Port Definitions
|
|
|
|
**Your first tasks**:
|
|
|
|
#### 1. Create Domain Entities
|
|
|
|
Create these files in `apps/backend/src/domain/entities/`:
|
|
|
|
```typescript
|
|
// organization.entity.ts
|
|
export class Organization {
|
|
constructor(
|
|
public readonly id: string,
|
|
public readonly name: string,
|
|
public readonly type: 'FREIGHT_FORWARDER' | 'NVOCC' | 'DIRECT_SHIPPER',
|
|
public readonly scac?: string,
|
|
public readonly address?: Address,
|
|
public readonly logoUrl?: string,
|
|
) {}
|
|
}
|
|
|
|
// user.entity.ts
|
|
export class User {
|
|
constructor(
|
|
public readonly id: string,
|
|
public readonly organizationId: string,
|
|
public readonly email: Email, // Value Object
|
|
public readonly role: UserRole,
|
|
public readonly passwordHash: string,
|
|
) {}
|
|
}
|
|
|
|
// rate-quote.entity.ts
|
|
export class RateQuote {
|
|
constructor(
|
|
public readonly id: string,
|
|
public readonly origin: PortCode, // Value Object
|
|
public readonly destination: PortCode, // Value Object
|
|
public readonly carrierId: string,
|
|
public readonly price: Money, // Value Object
|
|
public readonly surcharges: Surcharge[],
|
|
public readonly etd: Date,
|
|
public readonly eta: Date,
|
|
public readonly transitDays: number,
|
|
public readonly route: RouteStop[],
|
|
public readonly availability: number,
|
|
) {}
|
|
}
|
|
|
|
// More entities: Carrier, Port, Container, Booking
|
|
```
|
|
|
|
#### 2. Create Value Objects
|
|
|
|
Create these files in `apps/backend/src/domain/value-objects/`:
|
|
|
|
```typescript
|
|
// email.vo.ts
|
|
export class Email {
|
|
private constructor(private readonly value: string) {
|
|
this.validate(value);
|
|
}
|
|
|
|
static create(value: string): Email {
|
|
return new Email(value);
|
|
}
|
|
|
|
private validate(value: string): void {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(value)) {
|
|
throw new InvalidEmailException(value);
|
|
}
|
|
}
|
|
|
|
getValue(): string {
|
|
return this.value;
|
|
}
|
|
}
|
|
|
|
// port-code.vo.ts
|
|
export class PortCode {
|
|
private constructor(private readonly value: string) {
|
|
this.validate(value);
|
|
}
|
|
|
|
static create(value: string): PortCode {
|
|
return new PortCode(value.toUpperCase());
|
|
}
|
|
|
|
private validate(value: string): void {
|
|
// UN LOCODE format: 5 characters (CCCCC)
|
|
if (!/^[A-Z]{5}$/.test(value)) {
|
|
throw new InvalidPortCodeException(value);
|
|
}
|
|
}
|
|
|
|
getValue(): string {
|
|
return this.value;
|
|
}
|
|
}
|
|
|
|
// More VOs: Money, ContainerType, BookingNumber, DateRange
|
|
```
|
|
|
|
#### 3. Define Ports
|
|
|
|
**API Ports (domain/ports/in/)** - What the domain exposes:
|
|
|
|
```typescript
|
|
// search-rates.port.ts
|
|
export interface SearchRatesPort {
|
|
execute(input: RateSearchInput): Promise<RateQuote[]>;
|
|
}
|
|
|
|
export interface RateSearchInput {
|
|
origin: PortCode;
|
|
destination: PortCode;
|
|
containerType: ContainerType;
|
|
mode: 'FCL' | 'LCL';
|
|
departureDate: Date;
|
|
weight?: number;
|
|
volume?: number;
|
|
hazmat: boolean;
|
|
}
|
|
```
|
|
|
|
**SPI Ports (domain/ports/out/)** - What the domain needs:
|
|
|
|
```typescript
|
|
// rate-quote.repository.ts
|
|
export interface RateQuoteRepository {
|
|
save(rateQuote: RateQuote): Promise<void>;
|
|
findById(id: string): Promise<RateQuote | null>;
|
|
findByRoute(origin: PortCode, destination: PortCode): Promise<RateQuote[]>;
|
|
}
|
|
|
|
// carrier-connector.port.ts
|
|
export interface CarrierConnectorPort {
|
|
searchRates(input: RateSearchInput): Promise<RateQuote[]>;
|
|
checkAvailability(input: AvailabilityInput): Promise<boolean>;
|
|
}
|
|
|
|
// cache.port.ts
|
|
export interface CachePort {
|
|
get<T>(key: string): Promise<T | null>;
|
|
set<T>(key: string, value: T, ttl: number): Promise<void>;
|
|
delete(key: string): Promise<void>;
|
|
}
|
|
```
|
|
|
|
#### 4. Write Domain Tests
|
|
|
|
```typescript
|
|
// domain/services/rate-search.service.spec.ts
|
|
describe('RateSearchService', () => {
|
|
let service: RateSearchService;
|
|
let mockCache: jest.Mocked<CachePort>;
|
|
let mockConnectors: jest.Mocked<CarrierConnectorPort>[];
|
|
|
|
beforeEach(() => {
|
|
mockCache = createMockCache();
|
|
mockConnectors = [createMockConnector('Maersk')];
|
|
service = new RateSearchService(mockCache, mockConnectors);
|
|
});
|
|
|
|
it('should return cached rates if available', async () => {
|
|
const input = createTestRateSearchInput();
|
|
const cachedRates = [createTestRateQuote()];
|
|
mockCache.get.mockResolvedValue(cachedRates);
|
|
|
|
const result = await service.execute(input);
|
|
|
|
expect(result).toEqual(cachedRates);
|
|
expect(mockConnectors[0].searchRates).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should query carriers if cache miss', async () => {
|
|
const input = createTestRateSearchInput();
|
|
mockCache.get.mockResolvedValue(null);
|
|
const carrierRates = [createTestRateQuote()];
|
|
mockConnectors[0].searchRates.mockResolvedValue(carrierRates);
|
|
|
|
const result = await service.execute(input);
|
|
|
|
expect(result).toEqual(carrierRates);
|
|
expect(mockCache.set).toHaveBeenCalledWith(
|
|
expect.any(String),
|
|
carrierRates,
|
|
900, // 15 minutes
|
|
);
|
|
});
|
|
|
|
// Target: 90%+ coverage for domain
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Recommended Reading Order
|
|
|
|
Before starting development, read these in order:
|
|
|
|
1. **[QUICK-START.md](QUICK-START.md)** (5 min)
|
|
- Get everything running
|
|
|
|
2. **[CLAUDE.md](CLAUDE.md)** (30 min)
|
|
- Understand hexagonal architecture
|
|
- Learn the rules for each layer
|
|
- See complete examples
|
|
|
|
3. **[apps/backend/README.md](apps/backend/README.md)** (10 min)
|
|
- Backend-specific guidelines
|
|
- Available scripts
|
|
- Testing strategy
|
|
|
|
4. **[TODO.md](TODO.md)** - Sections relevant to current sprint (20 min)
|
|
- Detailed task breakdown
|
|
- Acceptance criteria
|
|
- Technical specifications
|
|
|
|
---
|
|
|
|
## 🛠️ Development Guidelines
|
|
|
|
### Hexagonal Architecture Rules
|
|
|
|
**Domain Layer** (`src/domain/`):
|
|
- ✅ Pure TypeScript classes
|
|
- ✅ Define interfaces (ports)
|
|
- ✅ Business logic only
|
|
- ❌ NO imports from NestJS, TypeORM, or any framework
|
|
- ❌ NO decorators (@Injectable, @Column, etc.)
|
|
|
|
**Application Layer** (`src/application/`):
|
|
- ✅ Import from `@domain/*` only
|
|
- ✅ Controllers, DTOs, Mappers
|
|
- ✅ Handle HTTP-specific concerns
|
|
- ❌ NO business logic
|
|
|
|
**Infrastructure Layer** (`src/infrastructure/`):
|
|
- ✅ Import from `@domain/*` only
|
|
- ✅ Implement port interfaces
|
|
- ✅ Framework-specific code (TypeORM, Redis, etc.)
|
|
- ❌ NO business logic
|
|
|
|
### Testing Strategy
|
|
|
|
- **Domain**: 90%+ coverage, test without any framework
|
|
- **Application**: 80%+ coverage, test DTOs and mappings
|
|
- **Infrastructure**: 70%+ coverage, test with test databases
|
|
|
|
### Git Workflow
|
|
|
|
```bash
|
|
# Create feature branch
|
|
git checkout -b feature/domain-entities
|
|
|
|
# Make changes and commit
|
|
git add .
|
|
git commit -m "feat: add Organization and User domain entities"
|
|
|
|
# Push and create PR
|
|
git push origin feature/domain-entities
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Success Criteria for Week 1-2
|
|
|
|
By the end of Sprint 1-2, you should have:
|
|
|
|
- [ ] All core domain entities created (Organization, User, RateQuote, Carrier, Port, Container)
|
|
- [ ] All value objects created (Email, PortCode, Money, ContainerType, etc.)
|
|
- [ ] All API ports defined (SearchRatesPort, CreateBookingPort, etc.)
|
|
- [ ] All SPI ports defined (Repositories, CarrierConnectorPort, CachePort, etc.)
|
|
- [ ] Domain services implemented (RateSearchService, BookingService, etc.)
|
|
- [ ] Domain unit tests written (90%+ coverage)
|
|
- [ ] All tests passing
|
|
- [ ] No TypeScript errors
|
|
- [ ] Code formatted and linted
|
|
|
|
---
|
|
|
|
## 💡 Tips for Success
|
|
|
|
### 1. Start Small
|
|
Don't try to implement everything at once. Start with:
|
|
- One entity (e.g., Organization)
|
|
- One value object (e.g., Email)
|
|
- One port (e.g., SearchRatesPort)
|
|
- Tests for what you created
|
|
|
|
### 2. Test First (TDD)
|
|
```typescript
|
|
// 1. Write the test
|
|
it('should create organization with valid data', () => {
|
|
const org = new Organization('1', 'ACME Freight', 'FREIGHT_FORWARDER');
|
|
expect(org.name).toBe('ACME Freight');
|
|
});
|
|
|
|
// 2. Implement the entity
|
|
export class Organization { /* ... */ }
|
|
|
|
// 3. Run the test
|
|
npm test
|
|
|
|
// 4. Refactor if needed
|
|
```
|
|
|
|
### 3. Follow Patterns
|
|
Look at examples in CLAUDE.md and copy the structure:
|
|
- Entities are classes with readonly properties
|
|
- Value objects validate in the constructor
|
|
- Ports are interfaces
|
|
- Services implement ports
|
|
|
|
### 4. Ask Questions
|
|
If something is unclear:
|
|
- Re-read CLAUDE.md
|
|
- Check TODO.md for specifications
|
|
- Look at the PRD.md for business context
|
|
|
|
### 5. Commit Often
|
|
```bash
|
|
git add .
|
|
git commit -m "feat: add Email value object with validation"
|
|
# Small, focused commits are better
|
|
```
|
|
|
|
---
|
|
|
|
## 📞 Need Help?
|
|
|
|
**Documentation**:
|
|
- [QUICK-START.md](QUICK-START.md) - Setup issues
|
|
- [CLAUDE.md](CLAUDE.md) - Architecture questions
|
|
- [TODO.md](TODO.md) - Task details
|
|
- [apps/backend/README.md](apps/backend/README.md) - Backend specifics
|
|
|
|
**Troubleshooting**:
|
|
- [INSTALLATION-STEPS.md](INSTALLATION-STEPS.md) - Common issues
|
|
|
|
**Architecture**:
|
|
- Read the hexagonal architecture guidelines in CLAUDE.md
|
|
- Study the example flows at the end of CLAUDE.md
|
|
|
|
---
|
|
|
|
## 🎉 You're Ready!
|
|
|
|
**Current Status**: ✅ Sprint 0 Complete
|
|
**Next Milestone**: Sprint 1-2 - Domain Layer
|
|
**Timeline**: 2 weeks
|
|
**Focus**: Create all domain entities, value objects, and ports
|
|
|
|
**Let's build something amazing! 🚀**
|
|
|
|
---
|
|
|
|
*Xpeditis MVP - Maritime Freight Booking Platform*
|
|
*Good luck with Phase 1!*
|