- Bank ABC want to provide a new feature on its website. The feature is to purchase prepaid data for a SIM card by getting a voucher code from a 3rd party. Anyone can purchase the prepaid data on the website without login.
- The 3rd party provides an API for the client to call via HTTP(s) protocol to get the voucher code. The API always returns a voucher code after 3 to 120 seconds, it depends on the network traffic at that time.
- The bank wants to build a new service(s) to integrate with that 3rd party. But it expects that the API will return voucher code or a message that says the request is being processed within 30 seconds.
- If the web application receives the voucher code, it will show on the website for customers to use. In case that the code can't be returned in-time, the customer can receive it via SMS later.
- The customer can check all the voucher codes that they have purchased by phone number on the website, but it needs to be secured.
- Assume that the payment has been done before the website call to the services to get the voucher code.
-
From Problem #1 → We need have a ThirdParty service.
-
From Problem #1 & Problem #4 → We need have a PhoneCarrier service.
-
Problem #2 & Problem #3 & Problem #4 → The main service (I named PurchaseData) must handle for the following 2 cases:
3.1. Network traffic good. ThirdParty service return less than 30 seconds. PurchaseData can response data voucher to user. 3.2. Network traffic bad. ThirdParty service return greater than 30 seconds. PurchaseData must stop the current job and have another job run after when ThirdParty service return data voucher will to send data voucher to the user by SMS.
-
From Analize #3.2 → There must be an intermediary service (I named DataVoucher) to continue waiting for the ThirdParty service to return the data voucher.
-
From Analize #3.2 & Analize #4 → How can PurchaseData know the Third Party has returned the data voucher to the user to send the SMS?
-
From the question in Analize #5. We can solve it by the following 2 ways:
6.1. DataVoucher notify to PurchaseData after ThirdParty response. 6.2. PurchaseData call DataVoucher to ask the data voucher from ThirdParty.
-
From Problem #5 → must use database.
-
From Problem #5 → must apply authentication.
-
From Problem #5 & Analize #8 → there is a contradictory problem. Can an authenticated user see the data of another user?
From Analize #6, we have 2 options to choose from. Each option has its own strengths and weaknesses.
- All logic is concentrated in PurchaseData
- Not dependent on Data Voucher notify
- PurchaseData simplified
→ PurchaseData active better than.
From Analize #9. I find the requirement above is unreasonable, so I made a little change to the more reasonable request:
- Authenticated requests to purchase data voucher from the user.
- Authenticated requests to get list of vouchers that have been purchased by user.
Based on the above requirements and my personal experience. I will choose a Technical stack as follows:
- Java 8
- Spring Boot 2
- MySql 5.6 + Spring JPA + Hibernate
- JWT + Spring Security + Jjwt
- Spring Cloud Sleuth
- Redis 6.0 + Spring Data + Jedis
- Gradle 6.7
- Docker + Docker-compose
- Junit5 + Mockito 3.6
Perform the following steps to run the test:
- Start docker-compose:
./docker-compose-up.sh -f
Use option -f to build jar files & docker images
- Login to get JWT token. I use HTTPie because it's readable than curl:
http POST 'http://localhost:8080/user/login?name=trile&password=123123'
- Login with invalid username or password:
http POST 'http://localhost:8080/user/login?name=trile&password=incorrect-pass'
- Purchase data:
http POST 'http://localhost:8080/purchase-data/' \
Authorization:'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0cmlsZSIsInVzZXJJZCI6MSwiZXhwIjoxNjA5ODIwNDM2fQ.kL0ABL5te5DEsBbEmRqgsK__nan4FGj0Q2gLhNh8FJEo0acqjMHY3-X22fRjJCZQglwnTeRzHPMqXdpul4-vjg'
At backend
- Purchase data with token invalid:
http POST 'http://localhost:8080/purchase-data/' \
Authorization:'Bearer token_invalid'
- List purchased data vouchers:
http GET 'http://localhost:8080/purchase-data/' \
Authorization:'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0cmlsZSIsInVzZXJJZCI6MSwiZXhwIjoxNjA5OTMxMjYzfQ.mp3pnFYvfcKHj9Pr3sQocGlSXfXowNvREZzyreQxhTYTMIfcNsNdHK4dgxhoFKNwjeGhiYcsN9zeYiQwQceYLg'
- List purchased data vouchers with token invalid:
http GET 'http://localhost:8080/purchase-data/' \
Authorization:'Bearer token_invalid'
- Test Tracing with B3 Propagation
http POST 'http://localhost:8080/purchase-data/' \
Authorization:'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0cmlsZSIsInVzZXJJZCI6MSwiZXhwIjoxNjA5OTMxMjYzfQ.mp3pnFYvfcKHj9Pr3sQocGlSXfXowNvREZzyreQxhTYTMIfcNsNdHK4dgxhoFKNwjeGhiYcsN9zeYiQwQceYLg' x-b3-traceid:'74c34ca310a4392e' \
b3:'80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90'
UnitTest classes: