Skip to content

Commit 93b1815

Browse files
committed
fix(archi): clean archi of the project
1 parent b57bea2 commit 93b1815

54 files changed

Lines changed: 396 additions & 342 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ RUN ./mvnw package -DskipTests -B
1818
FROM eclipse-temurin:25-jre
1919
WORKDIR /app
2020

21-
# Create a non-root user and group for running the application securely
21+
# Create a non-root userDTO and group for running the application securely
2222
RUN groupadd --system appgroup && useradd --system --gid appgroup appuser
2323

2424
# Copy the built JAR from the build stage using a stable pattern so version changes do not break the image build
2525
COPY --from=build /app/target/*.jar app.jar
2626

27-
# Ensure the non-root user owns the application files
27+
# Ensure the non-root userDTO owns the application files
2828
RUN chown -R appuser:appgroup /app
2929

30-
# Switch to the non-root user
30+
# Switch to the non-root userDTO
3131
USER appuser
3232

3333
# Document the port the application listens on

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ The Dockerfile uses two stages to produce a lightweight, secure final image:
7777
| Stage | Image | Role |
7878
|-------|-------|------|
7979
| **Build** | `eclipse-temurin:25-jdk` | Compiles the JAR with Maven (full JDK) |
80-
| **Run** | `eclipse-temurin:25-jre` | Runs the application (lightweight JRE, non-root user) |
80+
| **Run** | `eclipse-temurin:25-jre` | Runs the application (lightweight JRE, non-root userDTO) |
8181

8282
> **Why Eclipse Temurin?** Reference OpenJDK distribution: free, open-source, maintained by the Eclipse Foundation (Adoptium).
8383
84-
> **Security:** The final image runs as a non-root user (`appuser`), without source code or build tools.
84+
> **Security:** The final image runs as a non-root userDTO (`appuser`), without source code or build tools.
8585
8686
### Compose Profiles
8787

@@ -152,10 +152,10 @@ dummy.api.base-url=https://dummyjson.com/
152152
| Method | Path | Description | Status |
153153
|--------|--------------------------|-------------|--------|
154154
| `GET` | `/random-users?page=1&size=10&source=DUMMY` | Fetch and save users from selected external API source ||
155-
| `GET` | `/random-users/{id}` | Get user by ID ||
156-
| `POST` | `/random-users` | Create a new user ||
157-
| `PUT` | `/random-users/{id}` | Update user ||
158-
| `DELETE` | `/random-users/{id}` | Delete user ||
155+
| `GET` | `/random-users/{id}` | Get userDTO by ID ||
156+
| `POST` | `/random-users` | Create a new userDTO ||
157+
| `PUT` | `/random-users/{id}` | Update userDTO ||
158+
| `DELETE` | `/random-users/{id}` | Delete userDTO ||
159159
| `GET` | `/random-users/filter` | Filter users by criteria ||
160160

161161
### Example Request
@@ -167,10 +167,10 @@ curl -X GET "http://localhost:8080/random-users?page=1&size=10&source=DUMMY"
167167
# Fetch 10 users from randomuser.me and store them
168168
curl -X GET "http://localhost:8080/random-users?page=1&size=10&source=RANDOM_USER"
169169

170-
# Get user by ID
170+
# Get userDTO by ID
171171
curl -X GET "http://localhost:8080/random-users/1"
172172

173-
# Create user
173+
# Create userDTO
174174
curl -X POST "http://localhost:8080/random-users" \
175175
-H "Content-Type: application/json" \
176176
-d '{
@@ -190,7 +190,7 @@ curl -X GET "http://localhost:8080/random-users/filter?gender=MALE&nat=FR"
190190
# Filter users by firstname (partial match, case-insensitive)
191191
curl -X GET "http://localhost:8080/random-users/filter?firstname=john"
192192

193-
# Update user
193+
# Update userDTO
194194
curl -X PUT "http://localhost:8080/random-users/1" \
195195
-H "Content-Type: application/json" \
196196
-d '{
@@ -257,7 +257,7 @@ curl -X PUT "http://localhost:8080/random-users/1" \
257257
### Domain Service Ports
258258

259259
- `LocalUserService`: local persistence operations (save, read, delete) backed by PostgreSQL.
260-
- `RemoteUserService`: external user source contract used by use cases.
260+
- `RemoteUserService`: external userDTO source contract used by use cases.
261261

262262
### External Source Adapter
263263

@@ -431,8 +431,8 @@ This project integrates with **DummyJSON Users API**:
431431
- **Users Endpoint**: https://dummyjson.com/users
432432
- **Documentation**: https://dummyjson.com/docs/users
433433
- **Features**:
434-
- Paginated user retrieval (`limit`, `skip`)
435-
- Stable JSON schema for user fixtures
434+
- Paginated userDTO retrieval (`limit`, `skip`)
435+
- Stable JSON schema for userDTO fixtures
436436
- Fast sandbox API for development/testing
437437

438438
---
@@ -442,9 +442,9 @@ This project integrates with **DummyJSON Users API**:
442442
Contributions are welcome! Please follow these steps:
443443

444444
1. Fork the repository
445-
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
446-
3. Commit changes (`git commit -m 'Add amazing feature'`)
447-
4. Push to branch (`git push origin feature/amazing-feature`)
445+
2. Create a features branch (`git checkout -b features/amazing-features`)
446+
3. Commit changes (`git commit -m 'Add amazing features'`)
447+
4. Push to branch (`git push origin features/amazing-features`)
448448
5. Open a Pull Request
449449

450450
---

mvnw.cmd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<testcontainers.version>2.0.4</testcontainers.version>
3232
<skipDocker>false</skipDocker>
3333
<sonar.coverage.exclusions>**/*Application.java</sonar.coverage.exclusions>
34-
<sonar.test.exclusions>**/feature/SpringIntegrationTest.java</sonar.test.exclusions>
34+
<sonar.test.exclusions>**/features/SpringIntegrationTest.java</sonar.test.exclusions>
3535
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
3636
</properties>
3737

src/main/java/com/xpeho/spring_boot_java_random_user/data/converters/UserConverter.java

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,40 @@
22

33
import com.xpeho.spring_boot_java_random_user.data.models.api.dummy.DummyUserResultDTO;
44
import com.xpeho.spring_boot_java_random_user.data.models.api.randomuser.RandomUserResultDTO;
5-
import com.xpeho.spring_boot_java_random_user.data.models.database.User;
5+
import com.xpeho.spring_boot_java_random_user.data.models.database.UserDTO;
66
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
77
import org.springframework.stereotype.Service;
88

99

1010
@Service
1111
public class UserConverter {
1212
// Domain -> DAO
13-
public User toDao(UserEntity entity) {
14-
User user = new User();
15-
user.setId(entity.id());
16-
user.setGender(entity.gender());
17-
user.setFirstname(entity.firstname());
18-
user.setLastname(entity.lastname());
19-
user.setCivility(entity.civility());
20-
user.setEmail(entity.email());
21-
user.setPhone(entity.phone());
22-
user.setPicture(entity.picture());
23-
user.setNationality(entity.nat());
24-
return user;
13+
public UserDTO toDao(UserEntity entity) {
14+
UserDTO userDTO = new UserDTO();
15+
userDTO.setId(entity.id());
16+
userDTO.setGender(entity.gender());
17+
userDTO.setFirstname(entity.firstname());
18+
userDTO.setLastname(entity.lastname());
19+
userDTO.setCivility(entity.civility());
20+
userDTO.setEmail(entity.email());
21+
userDTO.setPhone(entity.phone());
22+
userDTO.setPicture(entity.picture());
23+
userDTO.setNationality(entity.nat());
24+
return userDTO;
2525
}
2626

2727
// DAO -> Domain
28-
public UserEntity toDomain(User user) {
28+
public UserEntity toDomain(UserDTO userDTO) {
2929
return new UserEntity(
30-
user.getId(),
31-
user.getGender(),
32-
user.getFirstname(),
33-
user.getLastname(),
34-
user.getCivility(),
35-
user.getEmail(),
36-
user.getPhone(),
37-
user.getPicture(),
38-
user.getNationality()
30+
userDTO.getId(),
31+
userDTO.getGender(),
32+
userDTO.getFirstname(),
33+
userDTO.getLastname(),
34+
userDTO.getCivility(),
35+
userDTO.getEmail(),
36+
userDTO.getPhone(),
37+
userDTO.getPicture(),
38+
userDTO.getNationality()
3939
);
4040
}
4141

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/User.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/models/database/UserDTO.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
@Entity
1111
@Table(name = "users")
12-
public class User {
12+
public class UserDTO {
1313
@Id
1414
@GeneratedValue(strategy = GenerationType.IDENTITY)
1515
@Column(name = "id")
@@ -32,7 +32,7 @@ public class User {
3232
private String nationality;
3333

3434
// Required by JPA
35-
public User() {
35+
public UserDTO() {
3636
// No initialization needed
3737
}
3838

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
11
package com.xpeho.spring_boot_java_random_user.data.services;
22

33
import com.xpeho.spring_boot_java_random_user.data.converters.UserConverter;
4-
import com.xpeho.spring_boot_java_random_user.data.models.database.User;
4+
import com.xpeho.spring_boot_java_random_user.data.models.database.UserDTO;
5+
import com.xpeho.spring_boot_java_random_user.data.sources.api.RemoteUserService;
56
import com.xpeho.spring_boot_java_random_user.data.sources.database.UserRepository;
67
import com.xpeho.spring_boot_java_random_user.data.sources.database.UserSpecifications;
8+
import com.xpeho.spring_boot_java_random_user.domain.entities.PaginatedUsers;
79
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
810
import com.xpeho.spring_boot_java_random_user.domain.entities.UserFilter;
9-
import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
11+
import com.xpeho.spring_boot_java_random_user.domain.enums.UserSource;
12+
import com.xpeho.spring_boot_java_random_user.domain.services.UserService;
13+
import org.springframework.data.domain.Page;
14+
import org.springframework.data.domain.Pageable;
1015
import org.springframework.stereotype.Service;
1116

17+
import java.io.IOException;
1218
import java.util.List;
19+
import java.util.Map;
1320
import java.util.Optional;
21+
import java.util.function.Function;
22+
import java.util.stream.Collectors;
1423
import java.util.stream.StreamSupport;
1524

1625
@Service
17-
18-
public class UserServiceImpl implements LocalUserService {
26+
public class UserServiceImpl implements UserService {
1927
private final UserRepository userRepository;
2028
private final UserConverter userConverter;
29+
private final Map<UserSource, RemoteUserService> remoteUserServices;
2130

22-
public UserServiceImpl(UserRepository userRepository, UserConverter userConverter) {
31+
public UserServiceImpl(
32+
UserRepository userRepository,
33+
UserConverter userConverter,
34+
List<RemoteUserService> remoteUserServices
35+
) {
2336
this.userRepository = userRepository;
2437
this.userConverter = userConverter;
38+
this.remoteUserServices = remoteUserServices.stream()
39+
.collect(Collectors.toMap(RemoteUserService::getSource, Function.identity()));
2540
}
2641

2742
@Override
2843
public List<UserEntity> saveAll(List<UserEntity> users) {
29-
List<User> daoUsers = users.stream().map(userConverter::toDao).toList();
30-
Iterable<User> saved = userRepository.saveAll(daoUsers);
44+
List<UserDTO> daoUserDTOS = users.stream().map(userConverter::toDao).toList();
45+
Iterable<UserDTO> saved = userRepository.saveAll(daoUserDTOS);
3146
return StreamSupport.stream(saved.spliterator(), false)
3247
.map(userConverter::toDomain)
3348
.toList();
@@ -41,8 +56,8 @@ public Optional<UserEntity> getById(long id) {
4156

4257
@Override
4358
public UserEntity save(UserEntity user) {
44-
User savedUser = userRepository.save(userConverter.toDao(user));
45-
return userConverter.toDomain(savedUser);
59+
UserDTO savedUserDTO = userRepository.save(userConverter.toDao(user));
60+
return userConverter.toDomain(savedUserDTO);
4661
}
4762

4863
@Override
@@ -51,9 +66,19 @@ public void deleteById(long id) {
5166
}
5267

5368
@Override
54-
public List<UserEntity> filterUsers(UserFilter filter) {
55-
return userRepository.findAll(UserSpecifications.byFilter(filter)).stream()
56-
.map(userConverter::toDomain)
57-
.toList();
69+
public Page<UserEntity> filterUsers(UserFilter filter, Pageable pageable) {
70+
return userRepository.findAll(UserSpecifications.byFilter(filter), pageable)
71+
.map(userConverter::toDomain);
72+
}
73+
74+
@Override
75+
public PaginatedUsers fetchAndSaveUsers(int page, int size, UserSource source) throws IOException {
76+
RemoteUserService remoteService = remoteUserServices.get(source);
77+
if (remoteService == null) {
78+
throw new IllegalStateException("No remote service configured for source: " + source);
79+
}
80+
PaginatedUsers response = remoteService.fetchUsers(page, size);
81+
saveAll(response.data());
82+
return response;
5883
}
5984
}

src/main/java/com/xpeho/spring_boot_java_random_user/domain/services/RemoteUserService.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/RemoteUserService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.xpeho.spring_boot_java_random_user.domain.services;
1+
package com.xpeho.spring_boot_java_random_user.data.sources.api;
22

33
import com.xpeho.spring_boot_java_random_user.domain.entities.PaginatedUsers;
44
import com.xpeho.spring_boot_java_random_user.domain.enums.UserSource;

src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/randomuser/RandomUserApiConfig.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/UserApiConfig.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
package com.xpeho.spring_boot_java_random_user.data.sources.api.randomuser;
1+
package com.xpeho.spring_boot_java_random_user.data.sources.api;
22

3+
import com.xpeho.spring_boot_java_random_user.data.sources.api.dummy.DummyUserApi;
4+
import com.xpeho.spring_boot_java_random_user.data.sources.api.randomuser.RandomUserApi;
35
import org.springframework.beans.factory.annotation.Qualifier;
46
import org.springframework.context.annotation.Bean;
57
import org.springframework.context.annotation.Configuration;
@@ -8,7 +10,21 @@
810
import retrofit2.converter.gson.GsonConverterFactory;
911

1012
@Configuration
11-
public class RandomUserApiConfig {
13+
public class UserApiConfig {
14+
15+
@Bean(name = "dummyUserRetrofit")
16+
public Retrofit dummyUserRetrofit(Environment env) {
17+
return new Retrofit.Builder()
18+
.baseUrl(env.getRequiredProperty("dummy.api.base-url"))
19+
.addConverterFactory(GsonConverterFactory.create())
20+
.build();
21+
}
22+
23+
@Bean
24+
public DummyUserApi dummyUserApi(@Qualifier("dummyUserRetrofit") Retrofit dummyUserRetrofit) {
25+
return dummyUserRetrofit.create(DummyUserApi.class);
26+
}
27+
1228
@Bean(name = "randomUserRetrofit")
1329
public Retrofit randomUserRetrofit(Environment env) {
1430
return new Retrofit.Builder()

src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/dummy/DummyUserApiConfig.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)