Auction Hub is a backend service that is designed to serve a system of online auctions, basically the buying, selling, and bidding of auction items in real time, with a comprehensive focus on security of the application and its scalability.
Project Structure
Using Spring Boot, OpenAPI and eclipse.jdt.ls, the Auction System is designed to be stable, secure and scalable. It follows an API-first approach to allow for its client to know exactly what to expect from the API.
The project follows the following structure which allows it to be modular and maintainable to reducing cost of refactoring in future along with a test suit that tests the essential components of the application.
auction-system/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── auction/
│ │ │ └── system/ # Main application package
│ │ │ ├── config/ # Spring Security configuration classes
│ │ │ ├── controller/ # API Controllers (delegates to generated interfaces)
│ │ │ ├── entity/ # JPA entities
│ │ │ ├── exception/ # Custom exception handling
│ │ │ ├── filter/ # Request filters (e.g. JWT authentication filter)
│ │ │ ├── mapper/ # MapStruct mappers
│ │ │ ├── repository/ # Spring Data JPA repositories
│ │ │ ├── security/ # Security related components
│ │ │ ├── service/ # Business logic interfaces
│ │ │ │ └── impl/ # Interface implementations
│ │ │ └── AuctionSystemApplication.java # Spring Boot main class
│ │ └── resources/
│ │ ├── api/
│ │ │ └── openapi.api-description.yaml # API description file
│ │ ├── static/
│ │ ├── templates/
│ │ └── application.yaml # Application properties
│ └── test/ # Test sources
│ └── java/
│ └── com/
│ └── auction/
│ └── system/
│ ├── service/
│ │ └── impl/ # Service related tests
│ └── testutil/ # Test utility classes
├── mvnw # Maven wrapper executable (Linux/MacOS)
├── mvnw.cmd # Maven wrapper executable (Windows)
└── pom.xml # Maven Project Object Model
OpenAPI
It uses OpenAPI spec 3.0.3 to define its contract. The API contract can be
viewed interactively either by running the server offline and making a request
to the swagger.html
endpoint, which redirects to a Swagger UI page running
locally. Or my directly inspecting the
openapi.api-description.yaml
fine in the project source code.
This approach allows the consumers of the API to beforehand know what to expect from the API and even generate client stubs for mocking and prototyping, saving time a cost of development.
The project workflow involves interacting with OpenAPI description file, if an
endpoint needs to be added, the API description file can be updated to reflect
that and the server stubs will be generated with
openapi-generator during
the generate-source
phase of Maven default
lifecycle.
Security
The API is secured using Spring
Security with best practices in
mind. It uses stateless authentication using JWTs (access_token
and
id_token
). To enable SSO using third-party authorization and openid providers
there are callback endpoints like /auth/{provider}/callback
that expects an
Authorization: Bearer <token>
header in the POST
request.
The security scheme for these endpoints in OpenAPI description file looks like this:
paths:
/auth/google/callback:
post:
security:
- oauth2google: []
/auth/apple/callback:
post:
security:
- oauth2apple: []
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Authorization using JWT issued by the backend.
bearerFormat: JWT
oauth2google:
type: http
scheme: bearer
description: Authentication using ID Tokens from the Google Authorization Server.
bearerFormat: ID Token (Google)
oauth2apple:
type: http
scheme: bearer
description: Authentication using ID Tokens from the Apple Authorization Server.
bearerFormat: ID Token (Apple)
security:
- bearerAuth: []
To have a deeper look at the security scheme have a look at the SecurityConfig.java and OAuth2SecurityConfig in the com.auction.system.config package.
Database and ORM
The application uses an in-memory H2 database for development, but it can be configured to use any database of the choice (e.g. MySQL).
# Example configuration for a local MySQL server
spring:
datasource:
url: jdbc:mysql://localhost:3306/auction_system
username: test
password: password
Along with this the proper JDBC driver for the particular database needs to be on the class path for the application to connect to the data source successfully.
<!-- in project object model (pom.xml) -->
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
The actual ORM (Object Relational Mapping) framework used in the application is
Hibernate, along with Spring Data
JPA to reduce effort of directly
interacting with Hibernate interface such as Session
and EntityManager
.
// Entity class
@Entity
@Table(name = "users", uniqueConstraints = {
@UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email")
})
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class UserEntity { // User entity from the application source code
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false)
private String username;
@Column(nullable = false)
// ...more fields
}
Using Spring Data JPA it is now easy to generate SQL just by defining a
UserRepository
that extends a JpaRepository
.
// Repository interface
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
Optional<UserEntity> findByUsername(String username);
Optional<UserEntity> findByEmail(String email);
}
Now the repository can be used like this:
// Service class
@Service
@RequiredArgsConstructor
public class DefaultUserService implements UserService {
// Inject using constructor injection
private final UserRepository userRepository;
private final UserMapper userMapper;
private final PasswordEncoder passwordEncoder;
//...methods
}