Reviewed by 6 specialized AI reviewers. Explore the diagram and the full per-section feedback below.
Loading diagramβ¦
There are meaningful high-level ideas for recommendations and chat, but the design misses too much of the core product flow to meet senior-level expectations. The inability to clearly model, expose, and operationalize match creation before chat is a fundamental gap, and the incomplete API, entity model, and capacity reasoning reinforce that concern.
Consistency stance is explicitly stated
The design does make an explicit CAP/consistency choice instead of leaving it implicit. Calling out high availability with eventual consistency is a reasonable starting point for chat-oriented flows where temporary replica lag is often acceptable.
Traffic shape is partially quantified
Stating 1M DAU and an approximate 1:1 read/write mix gives at least some workload framing. That is better than listing quality goals with no connection at all to expected usage.
Basic quality attributes are too incomplete
Have you considered what happens when you need to judge whether matching or chat is 'fast enough' or 'reliable enough'? The section mentions availability and consistency, but gives no latency target, uptime target, or durability expectation. Without concrete goals like message delivery latency or acceptable downtime, the NFRs do not actually constrain the design.
Eventual consistency is not justified per feature
Have you considered what happens if eventual consistency affects the matching flow itself? For chat, temporary lag may be acceptable, but for match creation you usually need a clearer guarantee to avoid duplicate, missing, or asymmetric matches between two users. The consistency model is stated globally, but not justified for which operations can tolerate staleness and which cannot.
Numbers do not translate into defensible targets
What happens when 1M DAU concentrates into peak hours? DAU alone does not tell you whether the system needs to handle hundreds or tens of thousands of requests per second. The workload numbers are too high-level to defend the chosen availability/consistency posture or to reason about whether chat and matching can meet expected performance.
Functional facts are mixed into NFRs
You could improve this by separating product scope from quality goals. 'Text only' is a functional constraint, not a non-functional target. Replacing it with measurable qualities such as p95 message send latency, match generation latency, and availability would make this section much stronger.
Read/write ratio needs feature-level breakdown
You could improve this by tying the 1:1 read/write estimate to actual behaviors under the stated assumptions. Matching traffic and chat traffic have very different access patterns, and combining them into one ratio makes it hard to reason about which parts of the system need low latency versus which can tolerate asynchronous processing.
Core nouns are mixed with features instead of domain entities
Have you considered what the actual persisted domain objects are in this system? 'Login/auth/signup' and 'Chat interface' are flows or UI surfaces, not core entities. For the matching and chat happy path, I would expect nouns like User/Profile, Match, Conversation/Chat, and Message. Without naming the underlying entities, it becomes unclear what data is created, stored, and queried at each step.
Matching flow is missing a match entity
What happens when two users are paired by the algorithm? Without an explicit Match entity, the system has no clear way to represent that relationship, track match status, or connect the match to downstream chat creation. The design would be stronger if it modeled a Match between two profiles and then linked chat off that object.
Chat is not broken into conversation and message entities
Have you considered what happens after users start chatting? 'Chat interface' does not define the underlying relationship or stored records. At minimum, the core flow needs a Conversation (or Chat) entity tying the matched users together and a Message entity for the actual exchanged content. Without those, the chat requirement is only represented as a screen, not as a domain model.
Relationships between entities are not defined
How do these concepts connect: does one Profile have many Recommendations, does one Match create one Conversation, does one Conversation contain many Messages? Right now the design lists nouns but does not define the 1:1 or 1:N relationships needed to trace the happy path from profile creation to recommendation to match to chat.
Recommendations may be derived rather than a core entity
You could improve this by clarifying whether 'Recommendations' is a stored entity or just the output of the matching algorithm. If it is persisted, define how it relates to a user/profile. If it is computed on demand, then it may not belong in the core entity list compared with more central nouns like Match or Message.
Basic scale numbers are too incomplete to size the system
You have a starting point with total users, daily swipes, and chat volume, but what happens when you need to size peak traffic? Daily totals alone do not tell you request rate. For example, 10M swipes/day could be a modest average load or a very spiky evening peak. You could improve this by converting DAU assumptions into average and peak QPS for swipes, match generation, chat sends, and chat delivery.
The chat concurrency math is inconsistent
Have you considered whether 20% of 1M users is 200k rather than 20k? That 10x difference materially changes connection count, message fanout, and infra sizing. If your active chat population is off by an order of magnitude, the rest of the capacity plan will underprovision the realtime layer.
No logical chain from users to infrastructure needs
Right now the numbers stop at daily events and rough storage, so it is hard to tell whether the proposed architecture fits the load. What happens when you need to decide database size, cache pressure, websocket server count, or network bandwidth? You could improve this by showing a chain like DAU -> swipes/day -> swipe write QPS -> match read/write QPS -> concurrent chat users -> messages/sec -> storage growth and bandwidth.
Message size assumption makes the bandwidth estimate unreliable
Have you considered what happens if the 1MB per message assumption is wrong? For text chat, 1MB per message is extremely large, so the resulting 40GB/day figure is not a useful basis for sizing. If messages are actually small text payloads, storage and bandwidth needs are very different; if attachments are included, you need separate assumptions for text versus media.
Peak realtime connection load is not estimated
You could improve this by estimating how many users are connected simultaneously for chat, not just how many chat in a day. Realtime systems are often constrained by concurrent connections and message bursts more than by daily message totals.
Storage growth is only partially covered
You could improve this by extending the calculation beyond daily chat volume. Swipes, matches, user profiles, and chat retention all contribute to long-term storage. Even a rough monthly or yearly growth estimate would make the capacity plan more actionable.
Component choices are not justified by the stated load
What happens when you need to explain why a particular database, cache, or queue is enough for this scale? Without translating the workload into read/write rates and concurrency, there is no basis for judging whether the chosen components are appropriately sized or overengineered.
Core matching flow is not actually usable through these APIs
How does a client initiate matching, accept or reject a recommendation, or confirm that two users are now matched and allowed to chat? With only /login, /recommendations/page=1, and /chat/, the core functional requirement 'users are matched based on an algorithm' is not exposed as a complete API flow, so the mobile app cannot reliably drive the product behavior.
Protocol choice for chat is unclear
Have you considered how real-time chat is supposed to work on mobile? /chat/ by itself does not say whether this is REST polling, WebSocket, or something else. For chat, the protocol matters a lot because it affects message delivery, latency, reconnect behavior, and how the client receives new messages after a match.
Resource design is too vague to support client integration
What exactly is /chat/ operating on: a conversation list, a specific conversation, or message send/receive? Similarly, /recommendations/page=1 is not clean resource-oriented design. You could improve this by using clearer resources such as /recommendations?page=1, /matches, /matches/{matchId}, /conversations, and /conversations/{id}/messages so the client knows what entity each route represents.
Basic operations for primary entities are missing
Have you considered the full lifecycle of the main entities here: recommendations, matches, conversations, and messages? The routes shown do not cover creating or acknowledging a match, fetching a user's active chats, sending a message, or retrieving message history, so the app would need to guess behavior that should be explicit in the API.
Pagination approach is underspecified
You do show paging for recommendations, which is directionally good, but what happens when the client needs the next page, reaches the end, or the recommendation set changes between requests? You could improve this by defining a consistent pagination contract such as cursor or page parameters plus response metadata like next_cursor or has_more.
HTTP method semantics are not defined
Have you considered which operations are GET versus POST versus other verbs? Without methods, the client cannot tell whether /login is creating a session, whether /chat/ is sending a message or fetching messages, or whether /recommendations is read-only. Ambiguous verb usage usually leads to hard-to-maintain client/server contracts.
Error handling contract is missing
What does the mobile client see when login fails, recommendations are exhausted, a user tries to chat before a match exists, or message send fails due to a disconnect? Without status codes or a structured error body, the client cannot distinguish retryable failures from user-facing errors, which makes mobile UX and retry behavior brittle.
Precomputed recommendations with cache
Using profile tags/filters plus precomputed recommendations and a recommendation cache is a sensible choice for the stated scale. It avoids running collaborative filtering synchronously on every swipe request and keeps the hot recommendation read path lightweight.
WebSocket-based chat with worker fan-out
The design separates chat into a messaging service with multiple workers and uses WebSocket delivery to clients. That is a reasonable high-level approach for low-latency 1:1 text chat instead of forcing every message through request/response polling.
Connection routing awareness for chat delivery
The Redis-style mapping of user to connected messaging server shows awareness of a real distributed chat problem: once chat servers are horizontally scaled, the system needs a way to route a message to the node holding the recipient's active connection.
Main request path is not fully connected end-to-end
What happens when the client asks for login or recommendations? The client goes to API Gateway and then Load Balancer, but there is no drawn path from the Load Balancer to Auth Service or Application Service, and no path from Application Service to the cache/profile store used for recommendations. At HLD level, the core request path should be traceable end-to-end; otherwise the design does not clearly show how matching requests complete.
Match creation flow is missing between recommendations and chat eligibility
The requirements say users are matched based on an algorithm and then can chat. Have you considered what actually creates and persists a mutual match? The design shows recommendation generation and chat delivery, but not the state transition from swipe/like decisions to a confirmed match that authorizes chat. Without that flow, the system can recommend users and send messages, but it does not clearly enforce 'chat only after matching.'
Orphaned and ambiguous components weaken the architecture
There is an unnamed unknown node referenced by the matching annotation, plus a NoSQL store mentioned in annotations for profiles/recommendations/swiping activity but not actually connected by arrows. Have you considered making those dependencies explicit? At senior level, floating or unnamed components make it hard to reason about ownership, failure modes, and data flow.
Message queue topology is unclear and may create delivery loops
What happens when a chat message is published to MessageQueue and the Messaging Service also subscribes back from the same queue? As drawn, the same service both publishes and consumes without a clear distinction between inbound processing, fan-out, retry, and persistence acknowledgment. Without clearer separation, you risk duplicate delivery, unclear retry semantics, or workers consuming their own events.
Single points of failure are not addressed for stateful chat routing
What happens when the Redis-like user-to-server mapping or a messaging worker dies? The design depends on that mapping to route messages to the correct WebSocket server, but there is no indication of replication, re-registration, or fallback delivery. If that state is lost or stale, messages may be routed to the wrong node or dropped for online users.
Recommendation freshness and cache invalidation are not thought through
Have you considered what happens after a user swipes, updates profile data, or changes location? Since recommendations are precomputed and cached, stale entries can easily be served unless there is an invalidation or regeneration strategy tied to swipe activity and profile changes. That matters directly to the matching experience.
Push notifications are separated, but trigger conditions are underspecified
You could improve this by clarifying when Application Service versus Messaging Service emits notification events. Right now Application Service writes to Notification Queue, while chat events originate in Messaging Service. A clearer event ownership model would make offline message notifications and match notifications easier to reason about.
Draw your architecture for Dating App / Tinder and get an instant hire/no-hire signal from 6 specialized AI reviewers β free to start.