I did some googling, and I was directed to Software Engineering to ask architecture questions. If you know of a different forum that could help me, please direct me to it
I recently started learning about microservices, and would like to build an experimental app (the backend) just for practice. I’ll explain the app requirements, and after that outline my microservices-based solutions (and some doubts/questions I have). I’d love to get your feedback, or your approach to building this app using microservices.
Please note: I am a beginner when it comes to microservices, and still learning. My solution might not be good, so I’d like to learn from you.
The App (Silly App):
The purpose of this app is to make sure users eat carrots four times a week. App admins create a carrot eating competition that starts on day x and ends 8 weeks after day x. Users can choose whether or not to participate in the competition. When a user joins the competition, they need to post a picture of themselves eating a carrot. The admin approves/rejects the picture. If approved, the carrot eating session counts towards the weekly goal, otherwise it does not. At the end of each week, participating users are billed 10ドル for each carrot eating session they missed (for example, if they only eat carrots two times that week, they’re billed 20ドル). That 20ドル goes into a "money bucket". At the end of two months, users who successfully ate carrots four times a week every single week divide the money in the bucket among themselves. For example, assume we have users A, B, C. User A missed all carrot eating sessions for two months (puts 40ドル a week in the money bucket, so 320ドル by the end of two months). Users B and C eat their carrots four times a week consistently for two months. So users B and C take home 320ドル/2 = 160ドル.
Simplification: I wanted to start simple. Forget about money. Forget about admin approval. We can add that later. For now, let’s focus on a very simplified version of the app.
- User can signup/login/logout to app
- When a user signs up, they are automatically enrolled into the next carrot eating competition
- Users can post an image of him/herself eating a carrot
- Users can see a feed of other users images (similar to instagram, except all pics are of people eating carrots)
- Users can access their profile - a page that displays how they’re doing in the competition: I.e,for each week, how many carrots they ate. And which weeks they failed at.
- At any point in time, users can access a page that shows who the current winners are (i.e, users who did not miss a carrot eating session yet).
Is this an appropriate simplification to start with?
Thinking Microservices - Asynchronous Approach:
Auth Service: Responsible for Authenticating User
Database:
- User Table: id, username, email, password
Routes:
- POST /users/new : signup
- POST /users/login: login
- POST /users/signout: signout
Events:
- Publishes
- User: created
Image Service: Responsible for Saving Images (upload to Amazon S3)
Database:
- User Table: userId, username
- Image Table: imageId, userId, dateUploaded, imageUrl
Routes:
- POST /users/:userId/images: Post new image
- GET /users/:userId/image/:imageId: Return a specific image
- GET /images: Return all images (Feed)
Events:
- Publishes:
- Image:created (userId, imageId, imageUrl, dateUploaded)
Competition Service: Responsible for managing competition
Database:
- Competition table: id, startDate, duration
- User table: id, username, competitionId, results (see below)
Routes:
- POST /competition: create a competition
- GET /competition/:competitionId/users/:userId: get results for a specific user
- GET /competition/:competitionId/users: get a list of users participating in competition (see below)
- GET /competition/:competitionId: get a list of winners, and for each looser how many workouts they missed
Events:
- Listens:
- User:created
- Image:created
In the database, user table, Results is the JSON equivalent of
results = {
week1: {
date: 'oct 20 2020 - oct 27 2020',
results: ['mon oct 20 2020', 'tue oct 21 2020', 'thur oct 23 2020'],
},
week2: {
date: 'oct 28 2020 - nov4 2020',
results: ['somedate', 'somedate', 'somedate', 'somedate'],
},
week3: {
date: 'nov 5 2020 - nov 12 2020',
results: [],
},
...
}
Better ideas on how to store this data appreciated
GET /competition/:competitionId returns
const results: {
winners: [{ userId: 'jkjl'; username: 'jkljkl' }, { userId: 'jkjl'; username: 'jkljkl' }];
loosers: [
{ userId: 'kffjl'; username: 'klj'; carrotDaysMissed: 3 },
{ userId: 'kl'; username: 'kdddfj'; carrotDaysMissed: 2 }
];
};
What do you think of this? How would you improve it? Or would you approach this from an entirely different way?
-
1As a quick tip, I would like to share with you the following speech given at Google Next: Martin Nally - Designing Quality APIs. From the perspective of REST API dos and don'ts, the interesting part starts at 15:55.Attila Viniczai– Attila Viniczai2020年10月21日 08:44:50 +00:00Commented Oct 21, 2020 at 8:44
1 Answer 1
I recommend that you first design your API (i.e., identifying services and methods, and their consumers) and avoid talking about data storage. With that, walk through your user stories and make sure that the API accounts for everything.
The most important consideration is going to be what parts of the API will be exposed externally, since modifying those in the future might require involving other businesses or end users; possibly paying customers. Those parts of the API should be as future-proof as possible. Internal-only parts of the API are quite a bit easier to change.
As a first implementation, you can just return canned responses from most of the service methods, e.g., a map from requests to responses. This will be useful for unit-testing, anyway.
As far as your design, I think you need more user-facing services that then deal with internal services to handle data retrieval and updates:
- A service for signing up. (Separate because it requires data updates in multiple places.)
- A service for signing in. (Might need to provide an authn token for other API calls.)
- A service for maintaining one's own account.
- A service for admin actions.
- A service for user actions other than account maintenance.
- A service for viewing things. (Separate because it will likely be read-only.)
The point of this is to separate what the user can do from how it's actually done. You don't want your mobile app making a bunch of calls to the API to perform a single action for the user; it's better to have one call for one user action.
This way you're free to change how things happen internally, which is critical for being able to scale the entire application.
For example:
- You might find that requests for data to display are orders of magnitude more frequent than requests to upload a photo. Putting the two in separate services allows you to replicate them independently to account for amount of traffic.
- You will likely want to limit write access to the databases as much as possible. Keeping reads in a separate service allows you to limit write access to services that only perform updates. (You would still want an internal service to manage the database, rather than having direct access by multiple services.)
- Sign-up and sign-in services might be more prone to security weaknesses, and therefore might need to be updated more often and more quickly. Keeping them separate prevents unrelated features from slowing down or blocking updates.
-
Maybe add an example on how scalability could affect on the API design decisionslennon310– lennon3102020年11月21日 04:09:37 +00:00Commented Nov 21, 2020 at 4:09
-
@lennon310 Added a few. Thanks for the suggestion!Kevin P. Barry– Kevin P. Barry2020年11月21日 19:02:33 +00:00Commented Nov 21, 2020 at 19:02
-
Good stuff on keeping things separate, +1lennon310– lennon3102020年11月22日 01:49:07 +00:00Commented Nov 22, 2020 at 1:49
Explore related questions
See similar questions with these tags.