Early Access: 87 spots left.

Claim
Low Level DesignSingle ResponsibilitySingle Responsibility: Refactor UserService

Course content

Single Responsibility: Refactor UserService

Medium·Tagssolidsingle-responsibilityrefactoring

Problem Statement

The starter `UserService` mixes three responsibilities into a single class: 1. Persistence: saving and retrieving users from an in-memory "database". 2. Notification: sending welcome emails, printed to stdout for this exercise. 3. Report formatting: rendering an HTML view of a user. Refactor the design so that each concern lives in its own class. After your refactor, the design must expose this exact public API. The validator will reflect on it. ```java class UserRepository { public void save(String userId, String email, String name); public java.util.Map<String, Object> findById(String userId); } class EmailNotifier { public void sendWelcome(String email, String name); // prints to stdout } class UserReportFormatter { public String renderHtml(String name, String email); } class UserService { public UserService(UserRepository repo, EmailNotifier notifier); public void registerUser(String userId, String email, String name); // delegates: persist via repo, then notify via notifier. } ``` Note that `UserReportFormatter` is not a dependency of `UserService`. Formatting a report is a different concern from registering a user, and keeping them separate is part of what this exercise teaches. The validator runs five checks: 1. `UserRepository` works in isolation. The validator instantiates it and verifies that `save` followed by `findById` round-trips correctly. 2. `EmailNotifier` works in isolation. The validator captures stdout from `sendWelcome` and verifies that the recipient and name appear. 3. `UserReportFormatter` works in isolation. The validator calls `renderHtml` and verifies that the inputs are reflected in the output. 4. `UserService` delegates correctly. The validator constructs `UserService(repo, notifier)`, calls `registerUser`, and then asserts that the repo persisted the user and that stdout contains the welcome email. 5. Single responsibility. The validator uses reflection to assert that each class declares only methods for its own concern. For example, `UserRepository` must not declare a `sendWelcome` method, and `EmailNotifier` must not declare a `save` method. If you keep everything in `UserService`, every test fails. The other classes remain empty stubs, so the validator cannot construct them in isolation.

Examples

Example 1
Input
UserRepository repo = new UserRepository(); EmailNotifier n = new EmailNotifier(); UserService svc = new UserService(repo, n); svc.registerUser("u1", "alice@example.com", "Alice"); repo.findById("u1")
Output
"{name=\"Alice\", email=\"alice@example.com\"}"
Why
After registration, the repository holds the new user and the notifier has printed a welcome message to stdout.
Example 2
Input
new UserReportFormatter().renderHtml("Alice", "alice@example.com")
Output
"\"<div class=\\\"user\\\"><p>Name: Alice</p><p>Email: alice@example.com</p></div>\""
Why
The formatter is independent. It does not touch persistence or notification.

Constraints

  • Refactor must produce exactly four top-level classes: UserService, UserRepository, EmailNotifier, UserReportFormatter.
  • UserService's only public methods are the constructor and registerUser.
  • UserService must not declare its own save / findById / sendWelcome / renderHtml methods.
  • EmailNotifier prints the welcome message to stdout (System.out).