Skip to content

Commit b2cae7a

Browse files
committed
fix(architecture): clean architecture of this project
1 parent 4f2b620 commit b2cae7a

30 files changed

Lines changed: 185 additions & 145 deletions

README.md

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-15-blue?logo=postgresql)](https://www.postgresql.org/)
88
[![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
99

10-
A REST API built with Spring Boot that fetches random users from [Random User API](https://randomuser.me/) and stores them in PostgreSQL.
10+
A REST API built with Spring Boot that fetches users from [DummyJSON Users API](https://dummyjson.com/users) and stores them in PostgreSQL.
1111

1212
**[Quick Start](#-quick-start)[API Docs](#-api-documentation)[Architecture](#-architecture)**
1313

@@ -58,6 +58,14 @@ POSTGRES_DB=your_database
5858
POSTGRES_PORT=5432
5959
```
6060

61+
### External API Configuration
62+
63+
`application.properties` uses:
64+
65+
```properties
66+
dummy.api.base-url=https://dummyjson.com/
67+
```
68+
6169
### Profiles
6270

6371
- **default**: PostgreSQL (production)
@@ -73,18 +81,18 @@ POSTGRES_PORT=5432
7381

7482
### Endpoints
7583

76-
| Method | Path | Description | Status |
77-
|--------|------|-------------|--------|
78-
| `GET` | `/random-users?count=500` | Fetch and save random users ||
79-
| `GET` | `/random-users/{id}` | Get user by ID ||
80-
| `POST` | `/random-users` | Create a new user ||
81-
| `PUT` | `/random-users/{id}` | Update user ||
82-
| `DELETE` | `/random-users/{id}` | Delete user ||
84+
| Method | Path | Description | Status |
85+
|--------|--------------------------|-------------|--------|
86+
| `GET` | `/random-users?count=30` | Fetch and save users from external API ||
87+
| `GET` | `/random-users/{id}` | Get user by ID ||
88+
| `POST` | `/random-users` | Create a new user ||
89+
| `PUT` | `/random-users/{id}` | Update user ||
90+
| `DELETE` | `/random-users/{id}` | Delete user ||
8391

8492
### Example Request
8593

8694
```bash
87-
# Fetch 10 random users
95+
# Fetch 10 users from external API and store them
8896
curl -X GET "http://localhost:8080/random-users?count=10"
8997

9098
# Get user by ID
@@ -168,6 +176,16 @@ curl -X PUT "http://localhost:8080/random-users/1" \
168176
PostgreSQL
169177
```
170178

179+
### Domain Service Ports
180+
181+
- `LocalUserService`: local persistence operations (save, read, delete) backed by PostgreSQL.
182+
- `RemoteUserService`: external user source contract used by use cases.
183+
184+
### External Source Adapter
185+
186+
- `DummyUserApi`: Retrofit client contract for DummyJSON endpoints.
187+
- `DummyUserServiceImpl`: adapter that calls `DummyUserApi`, maps DTOs, and returns `PaginatedUsers`.
188+
171189
### Project Structure
172190

173191
```
@@ -327,14 +345,15 @@ error: release version 25 not supported
327345

328346
## 🌐 External API
329347

330-
This project integrates with **Random User Generator API**:
348+
This project integrates with **DummyJSON Users API**:
331349

332-
- **URL**: https://randomuser.me/api/
333-
- **Documentation**: https://randomuser.me/documentation
350+
- **Base URL**: https://dummyjson.com/
351+
- **Users Endpoint**: https://dummyjson.com/users
352+
- **Documentation**: https://dummyjson.com/docs/users
334353
- **Features**:
335-
- Generate random users
336-
- Filter by nationality
337-
- Customize response fields
354+
- Paginated user retrieval (`limit`, `skip`)
355+
- Stable JSON schema for user fixtures
356+
- Fast sandbox API for development/testing
338357

339358
---
340359

@@ -365,7 +384,7 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
365384
## 🔗 Useful Links
366385

367386
- [Spring Boot Documentation](https://spring.io/projects/spring-boot)
368-
- [Random User API Docs](https://randomuser.me/documentation)
387+
- [DummyJSON Users API Docs](https://dummyjson.com/docs/users)
369388
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
370389
- [Docker Documentation](https://docs.docker.com/)
371390
- [SonarQube Documentation](https://docs.sonarqube.org/)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.xpeho.spring_boot_java_random_user.data.converters;
22

3-
import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResultDAO;
3+
import com.xpeho.spring_boot_java_random_user.data.models.api.dummy.DummyUserResultDTO;
44
import com.xpeho.spring_boot_java_random_user.data.models.db.User;
55
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
66
import org.springframework.stereotype.Service;
@@ -38,8 +38,8 @@ public UserEntity toDomain(User user) {
3838
);
3939
}
4040

41-
// API -> Domain
42-
public UserEntity fromApiModel(RandomUserResultDAO model) {
41+
// API DTO -> Domain
42+
public UserEntity fromApiModel(DummyUserResultDTO model) {
4343
return new UserEntity(
4444
null,
4545
model.getGender(),

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/RandomUserNameDAO.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/dummy/DummyUserNameDTO.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.xpeho.spring_boot_java_random_user.data.models.api;
1+
package com.xpeho.spring_boot_java_random_user.data.models.api.dummy;
22

3-
public class RandomUserNameDAO {
3+
public class DummyUserNameDTO {
44
private String title;
55
private String first;
66
private String last;
@@ -29,3 +29,4 @@ public void setLast(String last) {
2929
this.last = last;
3030
}
3131
}
32+

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/RandomUserPictureDAO.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/dummy/DummyUserPictureDTO.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.xpeho.spring_boot_java_random_user.data.models.api;
1+
package com.xpeho.spring_boot_java_random_user.data.models.api.dummy;
22

3-
public class RandomUserPictureDAO {
3+
public class DummyUserPictureDTO {
44
private String medium;
55

66
public String getMedium() {
@@ -11,3 +11,4 @@ public void setMedium(String medium) {
1111
this.medium = medium;
1212
}
1313
}
14+

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/RandomUserResponse.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/dummy/DummyUserResponse.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package com.xpeho.spring_boot_java_random_user.data.models.api;
1+
package com.xpeho.spring_boot_java_random_user.data.models.api.dummy;
22

33
import com.google.gson.annotations.SerializedName;
44

55
import java.util.List;
66

7-
public class RandomUserResponse {
7+
public class DummyUserResponse {
88
@SerializedName("users")
9-
private List<RandomUserResultDAO> users;
9+
private List<DummyUserResultDTO> users;
1010

1111
@SerializedName("total")
1212
private int total;
@@ -17,11 +17,11 @@ public class RandomUserResponse {
1717
@SerializedName("limit")
1818
private int limit;
1919

20-
public List<RandomUserResultDAO> getUsers() {
20+
public List<DummyUserResultDTO> getUsers() {
2121
return users;
2222
}
2323

24-
public void setUsers(List<RandomUserResultDAO> users) {
24+
public void setUsers(List<DummyUserResultDTO> users) {
2525
this.users = users;
2626
}
2727

@@ -49,3 +49,4 @@ public void setLimit(int limit) {
4949
this.limit = limit;
5050
}
5151
}
52+

src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/RandomUserResultDAO.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/models/api/dummy/DummyUserResultDTO.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.xpeho.spring_boot_java_random_user.data.models.api;
1+
package com.xpeho.spring_boot_java_random_user.data.models.api.dummy;
22

3-
public class RandomUserResultDAO {
3+
public class DummyUserResultDTO {
44
private String gender;
55
private String firstName;
66
private String lastName;
@@ -56,3 +56,4 @@ public void setImage(String image) {
5656
this.image = image;
5757
}
5858
}
59+

src/main/java/com/xpeho/spring_boot_java_random_user/data/services/UserServiceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import com.xpeho.spring_boot_java_random_user.data.models.db.User;
55
import com.xpeho.spring_boot_java_random_user.data.sources.database.UserRepository;
66
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
7-
import com.xpeho.spring_boot_java_random_user.domain.services.UserService;
7+
import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
88
import org.springframework.stereotype.Service;
99

1010
import java.util.List;
@@ -13,7 +13,7 @@
1313

1414
@Service
1515

16-
public class UserServiceImpl implements UserService {
16+
public class UserServiceImpl implements LocalUserService {
1717
private final UserRepository userRepository;
1818
private final UserConverter userConverter;
1919

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.xpeho.spring_boot_java_random_user.data.sources.api;
2+
3+
import com.xpeho.spring_boot_java_random_user.data.models.api.dummy.DummyUserResponse;
4+
import retrofit2.Call;
5+
import retrofit2.http.GET;
6+
import retrofit2.http.Query;
7+
8+
public interface DummyUserApi {
9+
@GET("/users")
10+
Call<DummyUserResponse> getUsers(@Query("limit") int limit, @Query("skip") int skip);
11+
}
12+

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
import retrofit2.converter.gson.GsonConverterFactory;
99

1010
@Configuration
11-
public class RandomUserApiConfig {
11+
public class DummyUserApiConfig {
1212
@Bean
13-
public Retrofit randomUserRetrofit(Environment env) {
14-
String baseUrl = env.getProperty("randomuser.api.base-url", "https://dummyjson.com/");
13+
public Retrofit dummyUserRetrofit(Environment env) {
14+
String baseUrl = env.getRequiredProperty("dummy.api.base-url");
1515
OkHttpClient client = new OkHttpClient.Builder().build();
1616
return new Retrofit.Builder()
1717
.baseUrl(baseUrl)
@@ -21,7 +21,8 @@ public Retrofit randomUserRetrofit(Environment env) {
2121
}
2222

2323
@Bean
24-
public RandomUserApi randomUserApi(Retrofit randomUserRetrofit) {
25-
return randomUserRetrofit.create(RandomUserApi.class);
24+
public DummyUserApi dummyUserApi(Retrofit dummyUserRetrofit) {
25+
return dummyUserRetrofit.create(DummyUserApi.class);
2626
}
2727
}
28+

src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/RandomUserProviderImpl.java renamed to src/main/java/com/xpeho/spring_boot_java_random_user/data/sources/api/DummyUserServiceImpl.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
package com.xpeho.spring_boot_java_random_user.data.sources.api;
22

33
import com.xpeho.spring_boot_java_random_user.data.converters.UserConverter;
4-
import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResponse;
5-
import com.xpeho.spring_boot_java_random_user.data.models.api.RandomUserResultDAO;
4+
import com.xpeho.spring_boot_java_random_user.data.models.api.dummy.DummyUserResponse;
5+
import com.xpeho.spring_boot_java_random_user.data.models.api.dummy.DummyUserResultDTO;
66
import com.xpeho.spring_boot_java_random_user.domain.entities.PaginatedUsers;
77
import com.xpeho.spring_boot_java_random_user.domain.entities.UserEntity;
8-
import com.xpeho.spring_boot_java_random_user.domain.services.RandomUserProvider;
8+
import com.xpeho.spring_boot_java_random_user.domain.services.RemoteUserService;
99
import org.springframework.stereotype.Service;
1010
import retrofit2.Response;
1111

1212
import java.io.IOException;
1313
import java.util.List;
1414

1515
@Service
16-
public class RandomUserProviderImpl implements RandomUserProvider {
17-
private final RandomUserApi randomUserApi;
16+
public class DummyUserServiceImpl implements RemoteUserService {
17+
private final DummyUserApi dummyUserApi;
1818
private final UserConverter userConverter;
1919

20-
public RandomUserProviderImpl(RandomUserApi randomUserApi, UserConverter userConverter) {
21-
this.randomUserApi = randomUserApi;
20+
public DummyUserServiceImpl(DummyUserApi dummyUserApi, UserConverter userConverter) {
21+
this.dummyUserApi = dummyUserApi;
2222
this.userConverter = userConverter;
2323
}
2424

2525
@Override
26-
public PaginatedUsers fetchRandomUsers(int page, int size) throws IOException {
26+
public PaginatedUsers fetchUsers(int page, int size) throws IOException {
2727
// Convert 1-based page index to 0-based skip offset
2828
int skip = (page - 1) * size;
29-
Response<RandomUserResponse> response = randomUserApi.getRandomUsers(size, skip).execute();
29+
Response<DummyUserResponse> response = dummyUserApi.getUsers(size, skip).execute();
3030
if (!response.isSuccessful() || response.body() == null) {
3131
throw new IOException("Failed to fetch users: " + response.code());
3232
}
33-
RandomUserResponse body = response.body();
34-
List<RandomUserResultDAO> users = body.getUsers();
33+
DummyUserResponse body = response.body();
34+
List<DummyUserResultDTO> users = body.getUsers();
3535
if (users == null) {
3636
throw new IOException("Failed to parse users from response");
3737
}
@@ -41,3 +41,4 @@ public PaginatedUsers fetchRandomUsers(int page, int size) throws IOException {
4141
return new PaginatedUsers(entities, body.getTotal(), body.getSkip(), body.getLimit());
4242
}
4343
}
44+

0 commit comments

Comments
 (0)