This is a code review and refactoring challenge. The candidate reviews existing code, identifies code smells, proposes improvements, and implements the most important ones. Focus on the com/foodtakeway/ package.
Pick 1-2 tasks matching the candidate's level. Let them explore the codebase for ~10 minutes before starting.
Context: Look at Order.java and OrderRequest.java. The fields have default (package-private) access and there are no getters/setters.
What to do:
- Make fields
privateinOrderandOrderRequest - Add proper getters and setters (or use Lombok)
- Add the missing
@Idannotation onOrderfor MongoDB compatibility
Acceptance Criteria:
- All fields are
private - Getters/setters are available (via Lombok or manually)
Orderhas an@Id-annotated field- The app still compiles and works
Hints:
- The project already has Lombok as a dependency — use
@Dataor@Getter/@Setter - Look at
Event.javain theexamplespackage for a clean Lombok example OrderServiceaccesses fields directly — it will need to use getters/setters after your change
Context: The codebase uses System.out.println() everywhere for logging. This is a common code smell.
What to do:
- Add SLF4J logger to
OrderServiceandOrderApi - Replace all
System.out.println()calls with appropriate log levels - Use parameterized messages (e.g.,
log.info("Order placed: {}", orderId))
Acceptance Criteria:
- No
System.out.println()in the main package - Uses
LoggerFactory.getLogger()or Lombok's@Slf4j - Log levels are appropriate (
infofor business events,errorfor failures)
Hints:
- SLF4J is included via Spring Boot — no extra dependency needed
- Lombok provides
@Slf4jannotation which creates alogfield automatically - Use
log.info(),log.warn(),log.error()instead ofSystem.out.println()
Context: POST /order accepts any body — even empty or negative amounts. There's no validation.
What to do:
- Add validation annotations to
OrderRequest(@NotBlank,@NotNull,@Positive) - Add
@Validto the controller method parameter - Return
400with a meaningful error message for invalid input
Acceptance Criteria:
userEmailis required and not blankamountmust be positive- Invalid requests return
400with field-level error details
Hints:
- The
jakarta.validation-apidependency is already in the POM - You may need
spring-boot-starter-validationfor runtime support - Look into
@ExceptionHandlerfor clean error responses
Context: The OrderApi returns HTTP 200 for order creation and a plain string "Order Placed!". This doesn't follow REST conventions.
What to do:
- Return
201 Createdfor successful order placement - Return a proper JSON response (not a plain string) — include order ID and status
- Remove the
/hellotest endpoint
Acceptance Criteria:
POST /orderreturns201with a JSON body containing order details- Response includes at least:
orderId,amount,status - No more plain string responses
Hints:
- Use
HttpStatus.CREATEDorResponseEntity.status(201).body(...) - Create a response DTO class (e.g.,
OrderResponse) - The
OrderService.placeOrder()currently returnsvoid— you'll need to change it to return the order
Context: OrderRequest is a nested class inside OrderApi.java. The code organization is poor — DTOs and controllers are mixed together.
What to do:
- Extract
OrderRequestinto its own file - Create a proper package structure (e.g.,
dto/orrequest/) - Add proper access modifiers, constructors, and consider using Lombok
- Discuss: What other DTOs might be needed?
Acceptance Criteria:
OrderRequestlives in its own file with proper package- Has private fields with getters/setters or Lombok annotations
- Controller still works with the extracted class
Hints:
- Follow Java conventions — one public class per file
- Think about
OrderResponsetoo if you have time - The
examples/event/Event.javashows good DTO structure
Context: Orders are stored in an ArrayList<Order> in the service. MongoDB is configured and available (see examples/ExampleMongoDB.java), but it's not used for orders.
What to do:
- Create an
OrderRepositoryextendingMongoRepository - Update
OrderServiceto use the repository instead ofArrayList - Add the missing
@Idannotation toOrderif not already done - Verify orders persist across requests
Acceptance Criteria:
- Orders are saved to MongoDB
OrderRepositoryfollows Spring Data conventions- The in-memory
ArrayListis removed - Orders survive application restart (when using Docker MongoDB)
Hints:
- Look at
ExampleMongoDB.javafor a working MongoDB example MongoRepository<Order, String>providessave(),findAll(),findById()out of the box- Make sure Docker Compose is running (
docker-compose up -d)
Context: Order IDs are generated with "ORD" + random.nextInt(1000). This has a high collision risk and is not production-ready.
What to do:
- Replace the random ID generation with a proper strategy
- Ensure uniqueness of order IDs
- Discuss trade-offs of different ID strategies
Acceptance Criteria:
- Order IDs are guaranteed unique
- ID format is meaningful and readable
- No
Randomused for ID generation
Hints:
UUID.randomUUID()is the simplest approach- If using MongoDB, you could use its auto-generated
ObjectId - Discuss: sequential IDs vs UUIDs vs custom formats — what are the trade-offs?
Context: Discount logic is hardcoded inline in processOrder(): if (order.amount > 100) order.amount *= 0.9. This is brittle and hard to test.
What to do:
- Extract discount logic into a dedicated class (e.g.,
DiscountServiceorDiscountCalculator) - Make the threshold and percentage configurable
- Write unit tests for the discount rules
Acceptance Criteria:
- Discount logic is in a separate, testable class
- Threshold (100) and percentage (10%) are not hardcoded
- Unit tests cover: amount > threshold, amount = threshold, amount < threshold
Hints:
- Use
@Valuewithapplication.yamlproperties for configuration - Consider the Strategy pattern if you want to support multiple discount types
- Focus on single responsibility — one class, one reason to change
Context: The only test is contextLoads(). There's no business logic test coverage.
What to do:
- Write unit tests for
OrderService.placeOrder() - Test the discount logic (orders above and below 100 EUR)
- Mock dependencies if needed
Acceptance Criteria:
- Tests run with
./mvnw test - At least 3 test cases covering different scenarios
- Tests verify: order creation, discount applied, discount not applied
Hints:
- Use
@ExtendWith(MockitoExtension.class)for mocking - Be careful —
placeOrder()callsThread.sleep(10000)which will slow tests - Consider refactoring the
longRunningOrderProcess()out of the Order or making it testable
Context: placeOrder() synchronously calls processOrder() which includes a 10-second blocking operation (Thread.sleep). This blocks the HTTP request thread and doesn't scale.
What to do:
- Decouple order placement from order processing
- Use an asynchronous approach — Spring
@Async, Kafka events, orCompletableFuture - The API should return immediately after placing the order
Acceptance Criteria:
POST /orderreturns instantly (< 1 second)- Order processing happens asynchronously in the background
- The order is initially saved as "unprocessed" and later marked as "processed"
- Discuss: What happens if processing fails? How would you add retry/dead-letter?
Hints:
- The
examples/ExampleKafka.javashows a working Kafka producer/consumer pattern - Spring's
@Asyncwith@EnableAsyncis the simplest approach - For Kafka: produce an "OrderPlaced" event, consume it in a separate listener that calls
processOrder() - Think about idempotency — what if the same order is processed twice?
Context: The entire application is flat — entity, service, controller, and DTO all in one package with poor separation of concerns.
What to do:
- Propose and implement a proper package/layer structure
- Separate concerns: API layer, service layer, domain layer, persistence layer
- Add interfaces where appropriate (e.g.,
OrderServiceinterface + implementation) - Discuss: How would this architecture support adding new features (e.g., restaurants, menus, delivery tracking)?
Acceptance Criteria:
- Clear package structure with separation of concerns
- Controller only handles HTTP, service handles business logic, repository handles persistence
- Domain objects are separate from API DTOs
- Discuss extensibility and where new features would fit
Hints:
- Common structures: by-layer (
controller/,service/,repository/) or by-feature (order/,restaurant/) - Discuss trade-offs of each approach
- Consider: Where does the discount logic go? Where does event dispatching go?
- Think about the Dependency Inversion Principle — inner layers shouldn't depend on outer layers
Context: The project has Kafka (Redpanda) infrastructure and example code, but the Order flow doesn't use it. Events are just printed to console with System.out.println.
What to do:
- Publish an
OrderPlacedEventto Kafka when an order is placed - Create a consumer that processes the order asynchronously
- Publish an
OrderProcessedEventwhen processing completes - Discuss: How would you handle failures, retries, and dead-letter topics?
Acceptance Criteria:
- Order placement publishes to a Kafka topic
- A listener consumes and processes orders asynchronously
- Events are proper DTOs (not strings) serialized as JSON
- Processing completion triggers a second event
POST /orderreturns instantly
Hints:
- Reference
ExampleKafka.javafor producer/consumer patterns - Use the existing
Event.javaas a base or create domain-specific event classes - Consider event schema: what fields should
OrderPlacedEventcontain? - Docker Compose must be running for Kafka to work