slogan3

Ontwerp een REST API met het Command Pattern voor gestructureerde verzoekafhandeling

description

Met deze prompt leer ik hoe ik een REST API kan ontwerpen die met het Command Pattern verzoeken gestructureerd afhandelt, wat leidt tot betere onderhoudbaarheid en uitbreidbaarheid. Het helpt problemen met complexe endpointlogica te verminderen door verantwoordelijkheden te scheiden en biedt een heldere structuur voor foutafhandeling en logging.

prompt

Help me een REST API te ontwerpen die het Command Pattern toepast om verzoeken gestructureerd af te handelen. Mijn API-doel: <beschrijf het doel van mijn API>. Beschrijf hoe ik commando-objecten kan modelleren, hoe endpo ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Zeker! Hieronder vind je een overzicht en ontwerp voor een REST API die het Command Pattern toepast voor een webshop-bestelsysteem. --- ### 1. Conceptueel ontwerp van het Command Pattern in een REST API **Doel:** Gestructureerd en uitbreidbaar verzoekenafhandelingsmechanisme waarbij elk type actie in een apart commando-object wordt gemodelleerd. **Werking:** - Elke API-aanroep wordt vertaald naar een specifiek Command-object. - Een centrale CommandHandler of Dispatcher voert het commando uit. - Logging, foutafhandeling en eventueel transactional management worden rondom de uitvoering geregeld. --- ### 2. Modellering van commando-objecten **Basisconcept:** Maak voor elke bewerking een aparte Command-klasse, bijvoorbeeld: ```java // Java-voorbeeld public interface Command { void execute(); } public class PlaatsBestellingCommand implements Command { private String klantId; private List<Product> producten; private String afleveradres; // Constructor, getters, setters public void execute() { // Logica voor het plaatsen van een bestelling } } ``` **Voorbeelden van commando's:** - `PlaatsBestellingCommand` - `AnnuleerBestellingCommand` - `UpdateBestellingStatusCommand` - `VerzendBestellingCommand` Elk commando bevat de benodigde data voor de actie en implementeert een `execute()`-methode. --- ### 3. REST API endpoints en aanroep van commando's **Voorbeeld endpoints:** | HTTP Verb | URL | Beschrijving | Comando-actie | |------------|----------------------------|----------------------------------------|----------------------------------------| | POST | `/bestellingen` | Nieuwe bestelling plaatsen | `PlaatsBestellingCommand` | | POST | `/bestellingen/{id}/annuleren` | Bestelling annuleren | `AnnuleerBestellingCommand` | | PUT | `/bestellingen/{id}/status` | Status bijwerken | `UpdateBestellingStatusCommand` | **Implementatie:** De REST-controller vertaalt inkomende JSON-gegevens naar command-objecten en stuurt deze door naar een handler. ```java @PostMapping("/bestellingen") public ResponseEntity<?> plaatsBestelling(@RequestBody PlaatsBestellingRequest request) { PlaatsBestellingCommand command = new PlaatsBestellingCommand( request.getKlantId(), request.getProducten(), request.getAfleveradres() ); commandHandler.handle(command); return ResponseEntity.ok().build(); } ``` --- ### 4. Uitvoering, logging en foutafhandeling **Uitvoering:** - Een centrale `CommandHandler` of Dispatcher ontvangt het command. - Het handler-antwoord kan een resultaat bevatten (bijvoorbeeld order-ID). **Logging:** - Log elke command-uitvoering met details (bezig met welke bestelling, gebruiker, timestamp). - Gebruik een AOP-aspect of middleware om logging automatisch toe te passen. **Foutafhandeling:** - Tijdens `execute()` kunnen uitzonderingen optreden. - Catch deze en zet ze om in passende HTTP-fouten (404, 400, 500). **Voorbeeld:** ```java public class CommandHandler { public void handle(Command command) { try { // Log start logger.info("Uitvoering commando: " + command.getClass().getSimpleName()); command.execute(); // Log success logger.info("Succesvolle uitvoering"); } catch (Exception e) { // Log fout logger.error("Fout tijdens uitvoeren", e); throw e; // of vertaal naar een geschikte HTTP-fout } } } ``` --- ### 5. Voordelen voor onderhoudbaarheid en uitbreidbaarheid - **Modulariteit:** Elke actie is geïsoleerd in een apart commando, waardoor wijzigingen beperkt blijven tot specifieke klassen. - **Uitbreidbaarheid:** Nieuwe acties toegevoegd door nieuwe command-klassen en endpoints te maken, zonder bestaande code te beïnvloeden. - **Testbaarheid:** Commando's kunnen los getest worden, inclusief logica en validatie. - **Centralisatie van logica:** De handler regelt logging, transacties en foutafhandeling op één plek. --- ### Samenvatting Door het Command Pattern te integreren in je REST API ontwerp creëer je een gestructureerde manier om verzoeken te verwerken, die makkelijk te onderhouden en uit te breiden is. Elke API-aanroep vertaalt naar een specifiek command-object dat door een handler wordt uitgevoerd, terwijl logging en foutafhandeling centraal worden geregeld. --- Wil je een volledige code-voorbeeld of verdere uitwerking van bepaalde onderdelen?
provider: deepseek
model: deepseek-chat
Zeker, ik help je graag een REST API te ontwerpen volgens het Command Pattern voor een webshop bestelsysteem. Hier is een gestructureerde aanpak: ## 1. Command Pattern Basisstructuur ### Basis Command Interface ```java public interface OrderCommand { void execute(); void undo(); CommandResult getResult(); String getCommandType(); } ``` ### Command Result Object ```java public class CommandResult { private boolean success; private String message; private Object data; private String errorCode; // constructors, getters & setters } ``` ## 2. Concrete Command Implementaties ### Bestelling Aanmaken Command ```java public class CreateOrderCommand implements OrderCommand { private OrderService orderService; private OrderRequest orderRequest; private CommandResult result; public CreateOrderCommand(OrderService orderService, OrderRequest orderRequest) { this.orderService = orderService; this.orderRequest = orderRequest; } @Override public void execute() { try { Order order = orderService.createOrder(orderRequest); this.result = new CommandResult(true, "Bestelling succesvol aangemaakt", order, null); } catch (Exception e) { this.result = new CommandResult(false, "Fout bij aanmaken bestelling", null, "ORDER_CREATE_ERROR"); } } @Override public void undo() { if (result.isSuccess()) { orderService.cancelOrder(((Order)result.getData()).getId()); } } @Override public CommandResult getResult() { return result; } @Override public String getCommandType() { return "CREATE_ORDER"; } } ``` ### Bestelling Annuleren Command ```java public class CancelOrderCommand implements OrderCommand { private OrderService orderService; private String orderId; private CommandResult result; @Override public void execute() { try { Order cancelledOrder = orderService.cancelOrder(orderId); this.result = new CommandResult(true, "Bestelling geannuleerd", cancelledOrder, null); } catch (Exception e) { this.result = new CommandResult(false, "Fout bij annuleren bestelling", null, "ORDER_CANCEL_ERROR"); } } @Override public void undo() { // Herstel de geannuleerde bestelling orderService.restoreOrder(orderId); } // overige methoden... } ``` ### Betaling Verwerken Command ```java public class ProcessPaymentCommand implements OrderCommand { private PaymentService paymentService; private PaymentRequest paymentRequest; private CommandResult result; @Override public void execute() { try { Payment payment = paymentService.processPayment(paymentRequest); this.result = new CommandResult(true, "Betaling verwerkt", payment, null); } catch (PaymentException e) { this.result = new CommandResult(false, "Betaling mislukt", null, "PAYMENT_FAILED"); } } // overige methoden... } ``` ## 3. Command Invoker met Logging en Foutafhandeling ```java @Service public class CommandInvoker { private static final Logger logger = LoggerFactory.getLogger(CommandInvoker.class); private final List<OrderCommand> commandHistory = new ArrayList<>(); public CommandResult executeCommand(OrderCommand command) { try { logger.info("Uitvoeren commando: {}", command.getCommandType()); command.execute(); CommandResult result = command.getResult(); if (result.isSuccess()) { commandHistory.add(command); logger.info("Commando succesvol uitgevoerd: {}", command.getCommandType()); } else { logger.error("Commando mislukt: {} - {}", command.getCommandType(), result.getMessage()); } return result; } catch (Exception e) { logger.error("Onverwachte fout bij uitvoeren commando: {}", command.getCommandType(), e); return new CommandResult(false, "Interne serverfout", null, "INTERNAL_ERROR"); } } public void undoLastCommand() { if (!commandHistory.isEmpty()) { OrderCommand lastCommand = commandHistory.remove(commandHistory.size() - 1); lastCommand.undo(); logger.info("Commando ongedaan gemaakt: {}", lastCommand.getCommandType()); } } } ``` ## 4. REST Endpoints ### Order Controller ```java @RestController @RequestMapping("/api/orders") public class OrderController { @Autowired private CommandInvoker commandInvoker; @Autowired private OrderService orderService; @PostMapping public ResponseEntity<CommandResult> createOrder(@RequestBody OrderRequest orderRequest) { CreateOrderCommand command = new CreateOrderCommand(orderService, orderRequest); CommandResult result = commandInvoker.executeCommand(command); return ResponseEntity.status(result.isSuccess() ? 201 : 400).body(result); } @PostMapping("/{orderId}/cancel") public ResponseEntity<CommandResult> cancelOrder(@PathVariable String orderId) { CancelOrderCommand command = new CancelOrderCommand(orderService, orderId); CommandResult result = commandInvoker.executeCommand(command); return ResponseEntity.ok(result); } @PostMapping("/{orderId}/payment") public ResponseEntity<CommandResult> processPayment(@PathVariable String orderId, @RequestBody PaymentRequest paymentRequest) { ProcessPaymentCommand command = new ProcessPaymentCommand(paymentService, paymentRequest); CommandResult result = commandInvoker.executeCommand(command); return ResponseEntity.ok(result); } @PostMapping("/undo") public ResponseEntity<String> undoLastCommand() { commandInvoker.undoLastCommand(); return ResponseEntity.ok("Laatste commando ongedaan gemaakt"); } } ``` ## 5. Uitbreiding: Command Factory ```java @Component public class CommandFactory { @Autowired private OrderService orderService; @Autowired private PaymentService paymentService; public OrderCommand createCommand(String commandType, Object request) { switch (commandType) { case "CREATE_ORDER": return new CreateOrderCommand(orderService, (OrderRequest) request); case "CANCEL_ORDER": return new CancelOrderCommand(orderService, (String) request); case "PROCESS_PAYMENT": return new ProcessPaymentCommand(paymentService, (PaymentRequest) request); default: throw new IllegalArgumentException("Onbekend commando type: " + commandType); } } } ``` ## 6. Geavanceerde Features ### Asynchrone Command Verwerking ```java @Async public CompletableFuture<CommandResult> executeCommandAsync(OrderCommand command) { return CompletableFuture.supplyAsync(() -> executeCommand(command)); } ``` ### Command Queue voor Batch Verwerking ```java @Component public class CommandQueue { private final Queue<OrderCommand> pendingCommands = new ConcurrentLinkedQueue<>(); public void addCommand(OrderCommand command) { pendingCommands.add(command); } @Scheduled(fixedRate = 5000) public void processQueue() { while (!pendingCommands.isEmpty()) { OrderCommand command = pendingCommands.poll(); commandInvoker.executeCommand(command); } } } ``` ## 7. Voordelen voor Onderhoudbaarheid en Uitbreidbaarheid ### 🎯 **Onderhoudbaarheid** - **Separation of Concerns**: Elk commando encapsuleert specifieke business logic - **Eenvoudig testbaar**: Commando's kunnen geïsoleerd worden getest - **Centrale logging**: Alle logging gebeurt op één plek (CommandInvoker) - **Consistente foutafhandeling**: Gestandaardiseerde error handling ### 🔧 **Uitbreidbaarheid** - **Nieuwe features**: Voeg simpelweg nieuwe command klassen toe - **Wijzigingen**: Pas individuele commando's aan zonder andere te beïnvloeden - **Complexe workflows**: Combineer meerdere commando's voor geavanceerde processen - **Undo/Redo**: Eenvoudige implementatie van geschiedenis functionaliteit ### 📊 **Monitoring en Debugging** ```java // Voorbeeld: Command metrics tracking @Component public class CommandMetrics { private final MeterRegistry meterRegistry; private final Map<String, Counter> successCounters = new ConcurrentHashMap<>(); private final Map<String, Counter> errorCounters = new ConcurrentHashMap<>(); public void recordCommandExecution(String commandType, boolean success) { String counterName = success ? "command.success" : "command.error"; Map<String, Counter> counters = success ? successCounters : errorCounters; counters.computeIfAbsent(commandType, type -> meterRegistry.counter(counterName, "command_type", type)) .increment(); } } ``` ## 8. Gebruik in Practice ### Voorbeeld API Aanroep: ```bash # Bestelling aanmaken POST /api/orders { "customerId": "123", "items": [ {"productId": "P001", "quantity": 2}, {"productId": "P002", "quantity": 1} ], "shippingAddress": "Straat 123, Amsterdam" } # Betaling verwerken POST /api/orders/ORD-123/payment { "paymentMethod": "CREDIT_CARD", "amount": 99.50, "cardToken": "tok_123456" } ``` Dit design biedt een robuuste, schaalbare en onderhoudbare foundation voor je bestelsysteem API. Het Command Pattern zorgt voor een duidelijke scheiding van verantwoordelijkheden en maakt toekomstige uitbreidingen eenvoudig.