Layered Architecutre and Integration Test in Spring and JPA
⭐️
- Layered Architecture
- Hexagonal Architecture
- Unit test 🆚 Integration test
- IoC, DI, AOP
- ORM, paradigm, Hibernate
- JPA
✅ Layered Architecutre in Spring
- ❓ why use layered architecture?
- separation of concerns
to organize code by responsitbility
- Presentation Layer
Controller: handles HTTP request & response - Business Layer
Service: business logic,transactionandrollback Persistence Layer
Repository, Data access: access DB- Controller 👉🏻 Service 👉🏻 Repository 👉🏻 Database
✅ Integration test
- 👎🏻 Unit test is limited to testing a single class in isolation
- In real life, several modules cooperate
- 👉🏻 check how multiple parts of the system interact together
✅ Spring basic concepts
✔️ Spring Bean
object that is created, managed, and stored inside the Spring IoC container
❌ This is not a Spring bean
1
2
3
4
5
UserService service = new UserService();
// I created it
// I manage it
// I control the life cycle
- ⭕️ Yes, this is a Spring bean
1
2
3
4
5
6
7
@Service
public class UserService {
}
// Spring creates it
// Spring injects it
// Spring controls its life cycle
Beans are sotred in
Spring IoC Container, calledApplication Context- 🧰 How to make a Spring bean
- 1️⃣ with annotations
1
2
3
4
5
@Component
@Service
@Repository
@Controller
@RestController
- 2️⃣ Using
@Beanin configuration class
1
2
3
4
5
6
7
8
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
- now
UserServiceis also a bean
✔️ Inversion of Control IoC
- Spring control objects
- control of creating and managing objects is transferred from your code to the Spring container
Spring creates and manages the object for you
👀 Manager assign task instead of chef choosing work
- 👎🏻 Before
1
2
3
public class UserController {
private UserService service = new UserService();
}
UserControllercontrols the creation of theUserService👎🏻 tight coupling
- 👍🏻 After
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//step 1. make UserService into a Spring Bean
@Service
public class UserService {
}
//step 2. Inject it using DI, constructor injection
@RestController
public class UserController {
private final UserService service;
public UserController(UserService service) {
this.service = service;
}
}
- Now, Spring creates object
UserService - Spring also creates
UserController Then, Spring injects
UserServiceintoUserController- Manages lifecycle
- Injects dependencies
- Handles configuration
- 👉🏻 Done by Spring IoC Container
✔️ Dependency Injection DI
- mechanism Spring uses to implement IoC
- create dependency inside class ❌
- inject dependency from outside ⭕️
👍🏻 loose coupling
👀 Manager gives ingredients to chef, instead of the chef buying them
- Types of DI
- constructor injection
- setter injection
field injection(not recommended)
- 👎🏻 Before
1
2
3
4
5
6
7
8
public class OrderService {
private PaymentService paymentService = new PaymentService();
public void placeOrder() {
paymentService.processPayment();
}
}
OrderServicedirectly createsPaymentServicehave to change the whole code if
PaymentServicechanges- 👍🏻 After
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//step 1. create interface
public interface PaymentService {
void processPayment();
}
//step 2. create implementations
@Service
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing credit card payment");
}
}
@Service
public class PayPalPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing PayPal payment");
}
}
//step 3. inject dependency via constructor
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void placeOrder() {
paymentService.processPayment();
}
}
// what order service needs is paying
// does not have to know what kind of payment, paypal or credit card
- Spring creates all beans
- Finds a class that implements
PaymentService - Injects it into
OrderService - Manages lifecycle automatically
✔️ Aspect Oriented Programming AOP
- add extra behavior without changing business logic
- separate cross-cutting concerns from business logic
cross-cutting concerns
- logging
- security
- transactions
- caching
- exception handling
- 👀 CCTV, billing, cleaning - applied everywhere automatically without changing cooking recipie
✅ Object Relational Mapping
ORM allows you to map
Java objects ↔ Database tables- Object oriended(
objects,fields,methods) and Relational DB(tables,rows,columns) are different paradigms - 👎🏻 Before:
- after developing, the devloper had to map all OOP data into DB
- code using pure JDBC
1
2
3
4
5
6
7
8
9
10
Connection conn = DriverManager.getConnection(...);
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?");
ps.setLong(1, 1L);
ResultSet rs = ps.executeQuery();
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
- 👍🏻 After:
- ORM acts as a bridge between them
1
2
3
4
5
6
7
8
@Entity
public class User {
@Id
private Long id;
private String name;
}
✅ JPA: Java Persistence API
- specification (rules/interface/standard) for ORM in Java
JPA defines:
- How Java objects should be mapped to database tables
- How persistence should work
- Standard APIs for CRUD operations
- JPA: rules(interface)
- Hibernate: implementation of the rules
- JPA can plug in other implementation(that are not Hibernate)and your JPA code still works
- normally JPA implements Hibernate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//step 1. ORM mapping
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
//step 2. implement JPA
public interface UserRepository extends JpaRepository<User, Long> {
}
//step 3. use JPA APIs for CRUD operations
userRepository.save(user);
userRepository.findById(1L);
- Important JPA concepts
1
2
3
4
5
6
@Entity
@Id
@Column
@ManyToOne, @OneToMany
@OneToOne, @ManyToMany
🆚 Hexagonal Architecture
- Domain is in the inner side
- surrounded by port
- ports are connected to adapter
- client, external system communicate through adapter
- 👍🏻 domain will not know what client is accessing
- Dependency Injection
This post is licensed under CC BY 4.0 by the author.