graph LR
	A["Client(Browser)"] -->|HTTP Request| B[@Controller]
	B --> C[@Service]
	C --> D[@Repository]
	D --> E[(Database)]
	E --> D
	D --> C
	C --> B
	B -->|HTTP Reponse| A
  • spring은 이 구조를 정말 철저하게 지킴!
    • controller, service, repository는 다 spring bean임

Diagram

graph LR

	subgraph User Application
		BR[Browser]
		JA[Java Application]
	end
	
	 Service Layer
	subgraph Service Layer
	    subgraph ProductService
	        PS1["findAllProducts()"]
	        PS2["findProductById()"]
	        PS3["saveProduct()"]
	        PS4["updateProduct()"]
	        PS5["deleteProduct()"]
	    end
	end
	
	 Database
	subgraph Database
	    DB1[(Product Table)]
			DB2[(User Table)]
	end
	
	%% 흐름 연결
	BR --> PC1
	BR --> PC2
	BR --> PC3
	BR --> PC4
	BR --> PC5
	JA --> PC1
	JA --> PC3
	PC1 --> PS1 --> PR1 --> DB1
	PC2 --> PS2 --> PR2 --> DB1
	PC3 --> PS3 --> PR3 --> DB1
	PC4 --> PS4 --> PR3 --> DB1
	PC5 --> PS5 --> PR4 --> DB1

Application Layers

LayerKey ResponsibilityDescription
ControllerReceives requests, returns responsesControls the flow of processing by accepting client requests.
ServiceProcesses business logicHandles core domain logic and manages transactions.
RepositoryAccesses dataPerforms direct communication with the database (using JPA, MyBatis, etc.).

@Controller / @RestController

  • 요청과 응답을 처리
  • CRUD
    • 조회할때 pw 빼기
AnnotationDescription
@ControllerUsed in View-based MVC applications.
- Typically paired with template engines like Thymeleaf (which is often the default choice)
- When a method returns a String, it’s interpreted as a view name to render.
@RestControllerDesigned for REST API responses.
- Automatically includes @ResponseBody, meaning that whatever a method returns will be directly written into the HTTP response body (e.g., as JSON or XML), rather than being used to resolve a view.
- @RestController = @Controller + @ResponseBody
  • @ResponseBody
  • @RequestBody
    • JSON → Java object, deserializes using Jackson
    • Used on method params for input parsing
@RestController // This implies @Controller + @ResponseBody
@RequestMapping("/hello")
public class HelloController {
 
    @GetMapping // Handles GET requests to /hello
    public String sayHello() {
        // Returns the string "Hello, World!" directly as the HTTP response body
        // Spring (via Jackson, implicitly) converts this to a JSON string if needed by the client.
        return "Hello, World!";
    }
}

@Service

  • The @Service annotation explicitly marks a class as a service component.
  • It’s where you implement your application’s core business logic, such as handling transactions and enforcing domain-specific rules
    • Use @Transactional annotation. Transactions are very important in backend applications (나중에 깊게 봄)
    • Business logic = Our requirements of the application
  • Depends on repository
@Service
public class ProductService {
 
    private final ProductRepository productRepository; // Injected by Spring
 
    // Constructor for dependency injection
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
 
    // Method containing business logic
    public List<Product> findAllProducts() {
        return productRepository.findAll(); // Delegates to the repository for data access
    }
}

@Repository

  • The @Repository annotation marks a class as a Data Access Object (DAO), meaning it directly communicates with the database.
    • This is the only component that directly communicates with the db
  • It essentially includes @Component internally and provides automatic exception translation. This means database-specific exceptions (like a SQLException) are converted into Spring’s consistent DataAccessException hierarchy, making your error handling more uniform
  • Commonly used with technology like Spring Data JPA (ORM 같은 기술 - 데이터를 객채로 나눔), MyBatis Mapper (SQL Mapper), etc
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    // Spring Data JPA automatically provides basic methods like findById, findAll, save, etc.
}

Dependency Relationships Between Layers

MUST follow a unidirectional dependencies between layers

Controller → Service → Repository
  • Things you MUST KEEP
    • You must avoid structures where the Repository refers to the Service, or the Service refers to the Controller.
    • Such circular dependencies can lead to structural issues and become a source of errors during testing and maintenance.
LayerDepends OnDependency Type
ControllerServiceConstructor Injection or Field Injection
ServiceRepositoryConstructor Injection
RepositoryNone (no lower layer)N/A

An incorrect dependency direction:

// BAD EXAMPLE: Avoid this!
@Repository
public class ProductRepository {
 
    @Autowired
    private ProductService productService;  // Prohibited: Do not reference Service from Repository
}

Dependencies Between Services in the Same Layer

  • It’s actually common for one Service to depend on another Service.
  • Ex) if you’re looking up order details and need information about the customer who placed the order, your OrderService might need to call MemberService
@Service
public class OrderService {
 
    private final MemberService memberService; // OrderService depends on MemberService
 
    @Autowired
    public OrderService(MemberService memberService) {
        this.memberService = memberService;
    }
 
    /* Business logic to place an order */
    public void placeOrder(Long memberId) {
 
        // 1. Retrieve information for the currently logged-in user
        //    (ID provided as a parameter)
        Member member = memberService.findById(memberId); // Calling MemberService
 
        // 2. Order placement logic...
    }
}
  • When you’re doing something like this, still keep these rules
    • Unidirectional Flow (no circular dependencies, Spring will give u error)
    • Clear responsibilities - Don’t let unrelated domains call each other directly—separate logic if needed
    • Respect Hierarchy - E.g., OrderService → MemberService is OK, but reverse might indicate poor design.
  • But what if we need to make MemberService also depend on OrderService?
    • If MemberService also depends on OrderService, there would be a circular dependency (순환참조), and Spring will not proceed & give an error.
    • Things you can do:
      • Make MemberService depend on OrderRepository instead
        • But this is not recommended tight coupling, hard to test/debug
          • MemberService will also have responsibilities of OrderRepository, leading to unclear boundary of responsibilities
          • If possible, a layer should only depend on another domain of the same layer
      • Make one of those 2 an Event
      • Make an intermediary class (explained below)

Intermediary class

Alternatively, you can design a structure where multiple domains can cooperate through a dedicated component:

/* Example of separating a collaboration-specific component */
@Component
public class OrderProcessManager {
 
    private final OrderService orderService;
    private final MemberService memberService;
 
    @Autowired
    public OrderProcessManager(OrderService orderService, MemberService memberService) {
        this.orderService = orderService;
        this.memberService = memberService;
    }
 
    public void placeOrderForMember(Long memberId) {
 
        Member member = memberService.findById(memberId);
        orderService.createOrder(member);
    }
}
  • This avoids direct mutual references between the two services by using OrderProcessManager
    • Put the collaborative logic to a separate coordinator.
    • The OrderProcessManager automatically has its dependencies wired by Spring via constructor injection, and it internally still references OrderService and MemberService
    • The key is that the services aren’t directly coupled; they interact through an intermediary (the coordinator). This allows each service to focus solely on its own responsibilities.

Practical Design Tips

  • Each layer should only depend on the layer(s) below it.
  • For DI (Dependency Injection), primarily use Constructor Injection, implemented with Lombok’s @RequiredArgsConstructor or Spring’s @Autowired.
  • When writing test code, clearly separating dependencies between layers makes Mocking and unit testing significantly easier.
    • Slice Test: Tests a specific layer in isolation (e.g., testing only the Controller). Dependent objects are replaced with fake objects (Mocking).
    • Unit Test: The smallest type of test, verifying that a single method works correctly.