CodeGym /Courses /Module 5. Spring /Brief summary of previous lectures

Brief summary of previous lectures

Module 5. Spring
Level 25 , Lesson 1
Available

Before you start writing code, it's important to pick the architectural style for the app, outline the main components and how they interact. Any experienced developer will tell you a poorly designed architecture is like a house without a foundation: it might stand for a bit, but not for long.


1. Architectural overview

Choosing an architectural style

We're building the app as a monolith. Yep, no microservices hype at this stage. Why? Because our app will be relatively small, and switching to microservices doesn't make sense. Everything will live in one project, but we'll split it into logical modules.

Application components

The parts of our app can be split into a few key blocks:

  1. REST API: we'll expose an HTTP interface for external consumers (e.g., the frontend).
  2. Service layer (business logic): this is where the main app logic lives, like data processing, validation, and transaction management.
  3. Database: for persisting data.
  4. Security: we'll protect access to our data and functionality.

Here's a sample component diagram:


+-------------------+
|   REST API        |
|-------------------|
|  Controllers      |
+-------------------+
        |
        v
+-------------------+
| Service layer     |
|-------------------|
|  Business logic   |
+-------------------+
        |
        v
+-------------------+
|  Data access      |
|-------------------|
|  JPA Repositories |
+-------------------+
        |
        v
+-------------------+
|  Database         |
|-------------------|

2. REST API design

First, remember that a REST API is an interface that lets you interact with the app using HTTP requests. Key REST principles:

  1. Resource-oriented: everything in our app is treated as a resource. For example, a user is the resource /users.
  2. HTTP methods: different methods for different operations:
    • GET to retrieve data.
    • POST to create new data.
    • PUT to update data.
    • DELETE to delete data.
  3. HTTP status codes: the server returns a status for the operation. For example:
    • 200 OK for a successful response.
    • 201 Created for successful resource creation.
    • 404 Not Found for a missing resource.

Endpoint design

We plan to create a basic CRUD for users. Here are the intended endpoints:

Method URL Description
GET /users Get list of users
GET /users/{id} Get user by ID
POST /users Create a new user
PUT /users/{id} Update user data
DELETE /users/{id} Delete a user

Here's how it looks in the controller:


@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping
    public List<User> getAllUsers() {
        // Returns list of all users
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // Gets user by id
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // Creates a new user
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // Updates user data
    }

    @DeleteMapping("/{id}")
    public ResponseEntity
    deleteUser(@PathVariable Long id) {
        // Deletes a user
    }
}

3. Application security

Implementing authentication using Spring Security

You already know Spring Security lets us add authentication and authorization rules.

1. Adding the dependency: In pom.xml add:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Creating the security configuration:


@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/users/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .httpBasic(); // We use basic authentication
    }
}

Here we protect the /users/** endpoints and leave the rest public.


4. Working with the database

Database design

For our app we need a users table. Example structure (ER diagram):

Field Data Type Description
id BIGINT Identifier
name VARCHAR User name
email VARCHAR Email address
password VARCHAR Password

Entity implementation

Create the User entity using JPA annotations:


@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    // Getters and setters
}

Repository for DB access

Use Spring Data JPA to work with data:


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

Now we can perform CRUD operations on user data via this interface.

Connection configuration

In application.properties we configure the database connection:


spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

Component interaction

So, how does it all tie together?

  1. A REST request hits the controller.
  2. The controller calls the appropriate method in the service layer.
  3. The service layer interacts with repositories and returns the result.
  4. The controller returns the response to the client.

Here's an example chain:


@RestController
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    }
}

At this point the application architecture is ready. Next we'll dive into decomposing the project tasks so we can figure out the best way to organize work on the remaining modules.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION