Architettura con Claude: Backend e Frontend
La progettazione dell'architettura è uno dei momenti in cui Claude eccelle maggiormente. Grazie al suo reasoning trasparente, Claude può aiutarti a esplorare trade-off, confrontare pattern architetturali e prendere decisioni informate che influenzeranno l'intero ciclo di vita del progetto.
In questo articolo vedremo come usare Claude per progettare architetture backend con Spring Boot e frontend con Angular, applicando le best practices di Solution Architecture.
Cosa Imparerai
- Usare Claude per decisioni architetturali con trade-off
- Progettare backend Spring Boot con Clean/Hexagonal Architecture
- Strutturare frontend Angular con pattern scalabili
- Definire API contract tra frontend e backend
- Gestire versioning ed error handling
Il Ruolo di Claude nell'Architettura
Claude può assumere il ruolo di Solution Architect è guidarti attraverso decisioni complesse. La chiave è fornire contesto sufficiente e chiedere esplicitamente l'analisi dei trade-off.
You are a Senior Solution Architect with 15+ years of experience
in enterprise software development.
EXPERTISE:
- Microservices architecture (Spring Boot, Kubernetes)
- Frontend architecture (Angular, React, Vue)
- Event-driven systems (Kafka, RabbitMQ)
- Cloud platforms (AWS, Azure, GCP)
- DDD, Clean Architecture, Hexagonal
COMMUNICATION STYLE:
- Always explain trade-offs for every recommendation
- Use diagrams (ASCII or Mermaid) when helpful
- Cite industry standards (12-factor app, SOLID, etc.)
- Challenge assumptions when they seem problematic
CONSTRAINTS:
- Prioritize maintainability over cleverness
- Consider team size and skill level
- Focus on practical, implementable solutions
- Always address security implications
Architettura Backend: Spring Boot
Pattern Architetturali Consigliati
Confronto Pattern Backend
| Pattern | Complessità | Quando Usarlo | Trade-off |
|---|---|---|---|
| Layered (MVC) | Bassa | CRUD semplici, MVP | Meno flessibile, accoppiamento alto |
| Clean Architecture | Media | Business logic complessa | Più codice, curva apprendimento |
| Hexagonal | Media-Alta | Integrazioni multiple, testabilità | Overhead iniziale, ports/adapters |
| CQRS | Alta | Read/write asimmetrici, scalabilità | Complessità infrastruttura |
Esempio: Hexagonal Architecture con Spring Boot
<role>Sei un Senior Backend Architect specializzato in Spring Boot</role>
<context>
Progetto: Order Management per e-commerce
Stack: Spring Boot 3.2, PostgreSQL, Redis, Kafka
Team: 4 developers mid-level
Requisiti:
- CRUD ordini con stati complessi
- Integrazione payment gateway (Stripe)
- Notifiche async (email, push)
- Audit trail completo
</context>
<task>
Progetta una Hexagonal Architecture per l'Order Service.
Includi:
1. Struttura package completa
2. Definizione Ports (inbound/outbound)
3. Adapters per ogni integration
4. Use Cases principali
</task>
<output_format>
- Diagramma ASCII o Mermaid
- Struttura cartelle
- Interfacce chiave con signature
- Spiegazione delle scelte
</output_format>
order-service/
├── src/main/java/com/company/orders/
│ ├── domain/ # Core Business Logic
│ │ ├── model/
│ │ │ ├── Order.java # Aggregate Root
│ │ │ ├── OrderItem.java
│ │ │ ├── OrderStatus.java # Enum stati
│ │ │ └── Money.java # Value Object
│ │ ├── service/
│ │ │ └── OrderDomainService.java
│ │ └── event/
│ │ └── OrderEvent.java # Domain Events
│ │
│ ├── application/ # Use Cases / Ports
│ │ ├── port/
│ │ │ ├── in/ # Inbound Ports
│ │ │ │ ├── CreateOrderUseCase.java
│ │ │ │ ├── UpdateOrderStatusUseCase.java
│ │ │ │ └── GetOrderQuery.java
│ │ │ └── out/ # Outbound Ports
│ │ │ ├── OrderRepository.java
│ │ │ ├── PaymentGateway.java
│ │ │ ├── NotificationSender.java
│ │ │ └── EventPublisher.java
│ │ └── service/
│ │ └── OrderApplicationService.java
│ │
│ ├── adapter/ # Infrastructure
│ │ ├── in/ # Inbound Adapters
│ │ │ ├── web/
│ │ │ │ ├── OrderController.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── CreateOrderRequest.java
│ │ │ │ │ └── OrderResponse.java
│ │ │ │ └── mapper/
│ │ │ │ └── OrderDtoMapper.java
│ │ │ └── event/
│ │ │ └── OrderEventListener.java
│ │ └── out/ # Outbound Adapters
│ │ ├── persistence/
│ │ │ ├── OrderJpaRepository.java
│ │ │ ├── OrderPersistenceAdapter.java
│ │ │ └── entity/
│ │ │ └── OrderJpaEntity.java
│ │ ├── payment/
│ │ │ └── StripePaymentAdapter.java
│ │ ├── notification/
│ │ │ ├── EmailNotificationAdapter.java
│ │ │ └── PushNotificationAdapter.java
│ │ └── messaging/
│ │ └── KafkaEventPublisher.java
│ │
│ └── config/ # Spring Configuration
│ ├── BeanConfiguration.java
│ ├── PersistenceConfig.java
│ └── KafkaConfig.java
Interfacce Chiave (Ports)
public interface CreateOrderUseCase {{ '{' }}
/**
* Crea un nuovo ordine validando disponibilità prodotti
* e iniziando il processo di pagamento.
*
* @param command contiene items, customer, payment info
* @return OrderId dell'ordine creato
* @throws InsufficientStockException se prodotti non disponibili
* @throws PaymentInitializationException se payment fallisce
*/
OrderId execute(CreateOrderCommand command);
{{ '}' }}
public record CreateOrderCommand(
CustomerId customerId,
List<OrderItemCommand> items,
ShippingAddress address,
PaymentMethod paymentMethod
) {{ '{' }}{{ '}' }}
public interface PaymentGateway {{ '{' }}
/**
* Inizia una transazione di pagamento.
* Non completa il pagamento, solo lo inizializza.
*
* @param request dati pagamento
* @return PaymentIntent con clientSecret per frontend
*/
PaymentIntent initializePayment(PaymentRequest request);
/**
* Conferma un pagamento già autorizzato.
*
* @param paymentIntentId ID del payment intent
* @return risultato conferma
*/
PaymentConfirmation confirmPayment(String paymentIntentId);
/**
* Esegue refund parziale o totale.
*/
RefundResult refund(String paymentIntentId, Money amount);
{{ '}' }}
Architettura Frontend: Angular
Pattern Consigliati per Angular
Pattern Frontend Angular
| Pattern | Complessità | Quando Usarlo |
|---|---|---|
| Feature-based | Bassa | Progetti piccoli/medi, team singolo |
| Smart/Dumb Components | Media | Riusabilita, testabilità |
| Signals + Services | Media | State management moderno, Angular 17+ |
| NgRx | Alta | Enterprise, stato complesso, debugging avanzato |
Esempio: Feature-based con Signals
src/app/
├── core/ # Servizi singleton
│ ├── services/
│ │ ├── api.service.ts # HTTP client base
│ │ ├── auth.service.ts
│ │ └── notification.service.ts
│ ├── guards/
│ │ └── auth.guard.ts
│ ├── interceptors/
│ │ ├── auth.interceptor.ts
│ │ └── error.interceptor.ts
│ └── models/
│ └── api-response.model.ts
│
├── shared/ # Componenti riutilizzabili
│ ├── components/
│ │ ├── button/
│ │ ├── modal/
│ │ ├── table/
│ │ └── form-field/
│ ├── pipes/
│ │ └── currency-format.pipe.ts
│ └── directives/
│ └── click-outside.directive.ts
│
├── features/ # Feature modules
│ ├── orders/
│ │ ├── components/
│ │ │ ├── order-list/ # Smart component
│ │ │ │ ├── order-list.component.ts
│ │ │ │ └── order-list.component.html
│ │ │ ├── order-card/ # Dumb component
│ │ │ │ ├── order-card.component.ts
│ │ │ │ └── order-card.component.html
│ │ │ └── order-detail/
│ │ ├── services/
│ │ │ └── order.service.ts # State + API
│ │ ├── models/
│ │ │ └── order.model.ts
│ │ └── orders.routes.ts
│ │
│ ├── products/
│ └── customers/
│
├── layouts/
│ ├── main-layout/
│ └── auth-layout/
│
└── app.routes.ts
Service con Signals
@Injectable({{ '{' }} providedIn: 'root' {{ '}' }})
export class OrderService {{ '{' }}
private http = inject(HttpClient);
// State
private ordersSignal = signal<Order[]>([]);
private loadingSignal = signal(false);
private errorSignal = signal<string | null>(null);
private selectedOrderSignal = signal<Order | null>(null);
// Public readonly signals
readonly orders = this.ordersSignal.asReadonly();
readonly loading = this.loadingSignal.asReadonly();
readonly error = this.errorSignal.asReadonly();
readonly selectedOrder = this.selectedOrderSignal.asReadonly();
// Computed signals
readonly pendingOrders = computed(() =>
this.ordersSignal().filter(o => o.status === 'PENDING')
);
readonly orderStats = computed(() => ({{ '{' }}
total: this.ordersSignal().length,
pending: this.pendingOrders().length,
totalRevenue: this.ordersSignal()
.filter(o => o.status === 'COMPLETED')
.reduce((sum, o) => sum + o.total, 0)
{{ '}' }}));
loadOrders(): void {{ '{' }}
this.loadingSignal.set(true);
this.errorSignal.set(null);
this.http.get<Order[]>('/api/orders').pipe(
catchError(err => {{ '{' }}
this.errorSignal.set('Errore nel caricamento ordini');
return of([]);
{{ '}' }}),
finalize(() => this.loadingSignal.set(false))
).subscribe(orders => this.ordersSignal.set(orders));
{{ '}' }}
createOrder(order: CreateOrderDto): Observable<Order> {{ '{' }}
return this.http.post<Order>('/api/orders', order).pipe(
tap(newOrder => {{ '{' }}
this.ordersSignal.update(orders => [...orders, newOrder]);
{{ '}' }})
);
{{ '}' }}
selectOrder(orderId: string): void {{ '{' }}
const order = this.ordersSignal().find(o => o.id === orderId);
this.selectedOrderSignal.set(order ?? null);
{{ '}' }}
{{ '}' }}
API Contract: Frontend-Backend
<task>
Definisci l'API contract REST per la gestione ordini.
Requisiti:
- RESTful con risorse annidate dove appropriato
- Paginazione per liste
- Error response standardizzate
- Versioning nell'URL (/api/v1/)
- HATEOAS links (opzionale)
</task>
<output_format>
Per ogni endpoint:
- Metodo HTTP + Path
- Request body (se POST/PUT)
- Response body con codici HTTP
- Esempi JSON
</output_format>
## Orders API v1
### Create Order
POST /api/v1/orders
Content-Type: application/json
Request:
{{ '{' }}
"customerId": "cust_123",
"items": [
{{ '{' }} "productId": "prod_456", "quantity": 2 {{ '}' }}
],
"shippingAddress": {{ '{' }}
"street": "Via Roma 1",
"city": "Milano",
"zipCode": "20100"
{{ '}' }},
"paymentMethod": "CARD"
{{ '}' }}
Response 201 Created:
{{ '{' }}
"id": "ord_789",
"status": "PENDING_PAYMENT",
"total": 99.99,
"createdAt": "2025-02-01T10:00:00Z",
"_links": {{ '{' }}
"self": {{ '{' }} "href": "/api/v1/orders/ord_789" {{ '}' }},
"payment": {{ '{' }} "href": "/api/v1/orders/ord_789/payment" {{ '}' }}
{{ '}' }}
{{ '}' }}
Response 400 Bad Request:
{{ '{' }}
"type": "validation_error",
"title": "Validation Failed",
"status": 400,
"errors": [
{{ '{' }}
"field": "items",
"message": "At least one item required"
{{ '}' }}
]
{{ '}' }}
### Get Orders (Paginated)
GET /api/v1/orders?page=0&size=20&status=PENDING
Response 200 OK:
{{ '{' }}
"content": [...],
"page": {{ '{' }}
"number": 0,
"size": 20,
"totalElements": 150,
"totalPages": 8
{{ '}' }}
{{ '}' }}
Error Handling Standardizzato
// Backend: GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {{ '{' }}
@ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ProblemDetail> handleNotFound(
OrderNotFoundException ex) {{ '{' }}
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND,
ex.getMessage()
);
problem.setType(URI.create("/errors/order-not-found"));
problem.setTitle("Order Not Found");
problem.setProperty("orderId", ex.getOrderId());
return ResponseEntity.status(404).body(problem);
{{ '}' }}
@ExceptionHandler(PaymentFailedException.class)
public ResponseEntity<ProblemDetail> handlePaymentFailed(
PaymentFailedException ex) {{ '{' }}
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.PAYMENT_REQUIRED,
"Payment processing failed"
);
problem.setType(URI.create("/errors/payment-failed"));
problem.setProperty("reason", ex.getReason());
problem.setProperty("retryable", ex.isRetryable());
return ResponseEntity.status(402).body(problem);
{{ '}' }}
{{ '}' }}
// error.interceptor.ts
export const errorInterceptor: HttpInterceptorFn = (req, next) => {{ '{' }}
const notification = inject(NotificationService);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {{ '{' }}
const problemDetail = error.error as ProblemDetail;
switch (error.status) {{ '{' }}
case 400:
notification.showError(
problemDetail.title ?? 'Validation Error'
);
break;
case 401:
// Redirect to login
break;
case 404:
notification.showWarning(
problemDetail.detail ?? 'Resource not found'
);
break;
case 500:
notification.showError(
'Server error. Please try again later.'
);
break;
{{ '}' }}
return throwError(() => error);
{{ '}' }})
);
{{ '}' }};
Checklist Architetturale
Checklist Prima di Iniziare lo Sviluppo
- Pattern architetturale backend definito e documentato
- Struttura cartelle frontend/backend concordata
- API contract documentato con esempi
- Error handling standardizzato (RFC 7807)
- Strategia di versioning API definita
- Convenzioni naming concordate
- Decisioni documentate in ADR
Conclusione e Prossimi Passi
In questo articolo abbiamo visto come usare Claude per progettare architetture solide per backend Spring Boot e frontend Angular. Abbiamo esplorato:
- Pattern architetturali e trade-off
- Hexagonal Architecture con Spring Boot
- Feature-based structure con Angular e Signals
- API contract design
- Error handling standardizzato
Nel prossimo articolo, approfondiremo la struttura del codice: organizzazione cartelle, naming conventions, separation of concerns e gestione configurazioni.
Punti Chiave da Ricordare
- Trade-off first: Chiedi sempre a Claude di analizzare pro/contro
- Hexagonal per integrazioni: Isola business logic da dettagli tecnici
- Signals per Angular 17+: State management semplice e reattivo
- API contract: Definiscilo prima di implementare
- Documenta le decisioni: Usa ADR per tracciare le scelte







