Gids voor het implementeren van een modulaire architectuur in TypeScript voor {{toepassing_type}}
description
Door deze gids te volgen, leer je je TypeScript-toepassing effectief structureren, waardoor het makkelijker wordt om te beheren, onderhouden en op te schalen. Dit verbetert de samenwerking binnen je team en de algehele codekwaliteit.
prompt
try_prompt
Help me begrijpen hoe ik een modulaire architectuur kan ontwerpen en implementeren in mijn TypeScript-toepassing voor {{toepassing_type}}. Geef gedetailleerde ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Het ontwerpen en implementeren van een modulaire architectuur in je TypeScript-webapp zorgt voor schaalbaarheid, onderhoudbaarheid en herbruikbaarheid van code. Hieronder geef ik een gedetailleerde uitleg, inclusief voorbeelden van module-structuur en best practices.
1. Basisprincipes van een modulaire architectuur
- Scheiding van zorgen: Verdeel functionaliteit in logische, onafhankelijke modules.
- Losse koppeling: Modules moeten zo min mogelijk afhankelijkheden hebben.
- Herbruikbaarheid: Modules moeten herbruikbaar en testbaar zijn.
- Centralisatie van configuratie en gedeelde code: Zorg voor een duidelijke structuur voor gedeelde resources.
2. Structuur van je project
Een goede structuur helpt bij de schaalbaarheid:
```plaintext
/my-webapp
│
├── src
│ ├── modules
│ │ ├── auth
│ │ │ ├── auth.ts
│ │ │ ├── auth.types.ts
│ │ │ └── index.ts
│ │ ├── user
│ │ │ ├── user.ts
│ │ │ ├── user.types.ts
│ │ │ └── index.ts
│ │ ├── products
│ │ │ ├── products.ts
│ │ │ ├── products.types.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── services
│ │ └── apiService.ts
│ ├── utils
│ │ └── helpers.ts
│ ├── main.ts
│ └── app.ts
│
├── package.json
└── tsconfig.json
```
3. Voorbeeld van module-implementatie
Stel dat je een module voor authenticatie hebt:
`src/modules/auth/auth.ts`
```typescript
export class AuthService {
login(username: string, password: string): boolean {
// login logica
return true;
}
logout(): void {
// logout logica
}
}
```
`src/modules/auth/index.ts`
```typescript
export * from './auth';
```
In je hoofdapplicatie kun je de module importeren:
```typescript
import { AuthService } from './modules/auth';
const auth = new AuthService();
auth.login('user', 'pass');
```
4. Beste praktijken
- **Gebruik index.ts bestanden**: exporteer alle relevante onderdelen van een module via een index.ts. Dit maakt importeren overzichtelijk.
- **Type definities**: scheid types/interfaces in aparte bestanden en gebruik ze consistent.
- **Configuratie en gedeelde code**: centraliseer gedeelde constants, API endpoints, en helpers.
- **Dependency management**: vermijd circular dependencies. Gebruik interfaces en dependency injection waar mogelijk.
- **Testbaarheid**: maak gebruik van unit tests voor elke module. Modules moeten klein en testbaar zijn.
- **Gebruik van namespaces of modules**: met ES6 modules (zoals hierboven) houd je code gescheiden en overzichtelijk.
- **Scalability**: voeg nieuwe modules toe in de `modules` directory zonder de bestaande code te beïnvloeden.
5. Voorbeeld van schaalbaarheid
Als je bijvoorbeeld een nieuwe module voor betalingen toevoegt:
- Maak een nieuwe map `payments` in `modules`.
- Voeg relevante bestanden toe (`payments.ts`, `payments.types.ts`, `index.ts`).
- Importeer en gebruik deze module in je hoofdapplicatie.
6. Samenvatting
Door je applicatie op te delen in kleine, goed gedefinieerde modules, zorg je voor een schaalbare en onderhoudsvriendelijke codebasis. Gebruik duidelijke structuur, scheid zorgen, en onderhoud consistente coding conventions.
Wil je een voorbeeld van volledige code of hulp bij het opzetten van een bepaald onderdeel?
provider: deepseek
model: deepseek-chat
Zeker! Hier een uitgebreide uitleg over het ontwerpen en implementeren van een modulaire architectuur in TypeScript voor webapplicaties.
## Fundamenten van Modulaire Architectuur
### 1. Wat is Modulaire Architectuur?
Modulaire architectuur betekent dat je je applicatie opdeelt in kleine, onafhankelijke eenheden (modules) die elk een specifieke verantwoordelijkheid hebben. Dit verbetert:
- **Onderhoudbaarheid**: Wijzigingen in één module hebben weinig impact op anderen
- **Testbaarheid**: Modules kunnen geïsoleerd worden getest
- **Herbruikbaarheid**: Modules kunnen in verschillende projecten worden hergebruikt
- **Schaalbaarheid**: Teams kunnen parallel werken aan verschillende modules
## Module Structuur en Organisatie
### 2. Aanbevolen Projectstructuur
```
src/
├── core/ # Kernfunctionaliteiten
│ ├── types/ # Globale TypeScript types
│ ├── utils/ # Hulpfuncties
│ ├── constants/ # Applicatieconstanten
│ └── config/ # Configuratie
├── modules/ # Business modules
│ ├── auth/ # Authenticatie module
│ ├── users/ # Gebruikersbeheer
│ ├── products/ # Productencatalogus
│ └── orders/ # Bestellingen
├── shared/ # Gedeelde componenten
│ ├── components/ # UI componenten
│ ├── hooks/ # Custom React hooks
│ └── services/ API services
└── app/ # Hoofdapplicatie
├── routes/ # Routing configuratie
└── layouts/ # Applicatie layouts
```
## Implementatie Voorbeelden
### 3. Basis Module Structuur
**Voorbeeld: Auth Module**
```typescript
// modules/auth/types/auth.types.ts
export interface User {
id: string;
email: string;
name: string;
}
export interface LoginCredentials {
email: string;
password: string;
}
export interface AuthState {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
}
```
```typescript
// modules/auth/services/auth.service.ts
import { LoginCredentials, User } from '../types/auth.types';
export class AuthService {
private baseUrl = '/api/auth';
async login(credentials: LoginCredentials): Promise<User> {
const response = await fetch(`${this.baseUrl}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials),
});
if (!response.ok) {
throw new Error('Login failed');
}
return response.json();
}
async logout(): Promise<void> {
await fetch(`${this.baseUrl}/logout`, { method: 'POST' });
}
}
```
```typescript
// modules/auth/hooks/useAuth.ts
import { useState, useEffect } from 'react';
import { AuthService } from '../services/auth.service';
import { AuthState, LoginCredentials } from '../types/auth.types';
export const useAuth = () => {
const [authState, setAuthState] = useState<AuthState>({
user: null,
isAuthenticated: false,
isLoading: true,
});
const authService = new AuthService();
useEffect(() => {
checkAuthentication();
}, []);
const login = async (credentials: LoginCredentials) => {
try {
const user = await authService.login(credentials);
setAuthState({ user, isAuthenticated: true, isLoading: false });
} catch (error) {
setAuthState(prev => ({ ...prev, isLoading: false }));
throw error;
}
};
const logout = async () => {
await authService.logout();
setAuthState({ user: null, isAuthenticated: false, isLoading: false });
};
const checkAuthentication = async () => {
// Implementeer sessie controle
};
return { ...authState, login, logout };
};
```
### 4. Dependency Injection en Loose Coupling
```typescript
// core/di/container.ts
export class DIContainer {
private services = new Map();
register<T>(token: string, service: T): void {
this.services.set(token, service);
}
resolve<T>(token: string): T {
const service = this.services.get(token);
if (!service) {
throw new Error(`Service ${token} not found`);
}
return service;
}
}
// Globale container instantie
export const container = new DIContainer();
```
```typescript
// modules/products/services/product.service.ts
export interface IProductService {
getProducts(): Promise<Product[]>;
getProductById(id: string): Promise<Product>;
}
export class ProductService implements IProductService {
// Implementatie
}
```
## Beste Praktijken
### 5. Dependency Management
```typescript
// modules/orders/services/order.service.ts
export class OrderService {
constructor(
private productService: IProductService,
private userService: IUserService
) {}
async createOrder(orderData: OrderData): Promise<Order> {
// Gebruik geïnjecteerde services
const product = await this.productService.getProductById(orderData.productId);
const user = await this.userService.getCurrentUser();
// Order logica
return await this.saveOrder({ product, user, ...orderData });
}
}
```
### 6. Event-Based Communicatie
```typescript
// core/events/event-bus.ts
type EventCallback = (data: any) => void;
export class EventBus {
private listeners: Map<string, EventCallback[]> = new Map();
on(event: string, callback: EventCallback): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(callback);
}
emit(event: string, data?: any): void {
const callbacks = this.listeners.get(event) || [];
callbacks.forEach(callback => callback(data));
}
}
export const eventBus = new EventBus();
```
### 7. Configuration Management
```typescript
// core/config/app-config.ts
export interface AppConfig {
api: {
baseUrl: string;
timeout: number;
};
features: {
enableNewUI: boolean;
enableAnalytics: boolean;
};
}
export const appConfig: AppConfig = {
api: {
baseUrl: process.env.REACT_APP_API_URL || 'http://localhost:3000',
timeout: 10000,
},
features: {
enableNewUI: process.env.REACT_APP_ENABLE_NEW_UI === 'true',
enableAnalytics: process.env.REACT_APP_ENABLE_ANALYTICS === 'true',
},
};
```
## Geavanceerde Patronen
### 8. Module Federatie (voor micro-frontends)
```typescript
// webpack.config.js voor Module Federatie
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
auth: 'auth@http://localhost:3001/remoteEntry.js',
products: 'products@http://localhost:3002/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};
```
### 9. State Management met Modules
```typescript
// modules/cart/store/cart.store.ts
import { create } from 'zustand';
import { Cart, CartItem } from '../types/cart.types';
interface CartState {
cart: Cart;
addItem: (item: CartItem) => void;
removeItem: (itemId: string) => void;
clearCart: () => void;
}
export const useCartStore = create<CartState>((set, get) => ({
cart: { items: [], total: 0 },
addItem: (item: CartItem) => {
const { cart } = get();
const existingItem = cart.items.find(i => i.id === item.id);
if (existingItem) {
existingItem.quantity += item.quantity;
} else {
cart.items.push(item);
}
cart.total = calculateTotal(cart.items);
set({ cart: { ...cart } });
},
removeItem: (itemId: string) => {
const { cart } = get();
cart.items = cart.items.filter(item => item.id !== itemId);
cart.total = calculateTotal(cart.items);
set({ cart: { ...cart } });
},
clearCart: () => {
set({ cart: { items: [], total: 0 } });
},
}));
```
## Testing Strategie
### 10. Module Testing
```typescript
// modules/auth/services/__tests__/auth.service.test.ts
import { AuthService } from '../auth.service';
import { LoginCredentials } from '../../types/auth.types';
describe('AuthService', () => {
let authService: AuthService;
beforeEach(() => {
authService = new AuthService();
global.fetch = jest.fn();
});
it('should login successfully with valid credentials', async () => {
const credentials: LoginCredentials = {
email: 'test@example.com',
password: 'password123',
};
const mockUser = { id: '1', email: 'test@example.com', name: 'Test User' };
(global.fetch as jest.Mock).mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockUser),
});
const result = await authService.login(credentials);
expect(result).toEqual(mockUser);
expect(global.fetch).toHaveBeenCalledWith(
'/api/auth/login',
expect.objectContaining({
method: 'POST',
body: JSON.stringify(credentials),
})
);
});
});
```
## Build en Bundling Optimalisaties
### 11. Code Splitting
```typescript
// app/routes/app-routes.tsx
import { lazy } from 'react';
const ProductsModule = lazy(() => import('../../modules/products/components/ProductsModule'));
const OrdersModule = lazy(() => import('../../modules/orders/components/OrdersModule'));
export const AppRoutes = () => (
<Routes>
<Route path="/products" element={
<Suspense fallback={<div>Loading...</div>}>
<ProductsModule />
</Suspense>
}/>
<Route path="/orders" element={
<Suspense fallback={<div>Loading...</div>}>
<OrdersModule />
</Suspense>
}/>
</Routes>
);
```
## Belangrijkste Richtlijnen
### 12. Best Practices Checklist
- ✅ **Eén verantwoordelijkheid per module**
- ✅ **Duidelijke module grenzen definiëren**
- ✅ **Dependency injection gebruiken voor loose coupling**
- ✅ **Standaard interfaces voor communicatie tussen modules**
- ✅ **Comprehensive testing per module**
- ✅ **Documenteer module APIs en contracts**
- ✅ **Gebruik TypeScript strikt voor type safety**
- ✅ **Implementeer error boundaries rond modules**
- ✅ **Monitor module prestaties en dependencies**
### 13. Veelgemaakte Fouten te Vermijden
- ❌ **Tight coupling tussen modules**
- ❌ **Globale state misuse**
- ❌ **Circular dependencies**
- ❌ **Over-engineered abstractions**
- ❌ **Niet consistente naming conventions**
- ❌ **Verwaarloosde documentatie**
Deze architectuur zal je helpen een schaalbare en onderhoudbare TypeScript-applicatie te bouwen die eenvoudig kan groeien met je bedrijfsbehoeften. Begin klein en refactor geleidelijk naar deze structuur als je applicatie complexer wordt.

