PWASocial Tech
Context
Responsible for chat functionality in team development. Built a prototype using Vanilla JS and Firebase, prioritizing development speed and validation in the initial phase. Following verification, a fundamental redesign focused on type safety and data integrity was decided to ensure long-term scalability and operational stability.
Why Rebuild
Based on the existing implementation, the necessity for a redesign was identified from the following perspectives:
- Lack of Type Safety
Changes in data structures were not detected until runtime, posing a challenge to maintainability.
- Client-Dependent Data Integrity
Authorization and state management partially relied on client-side logic, raising concerns about consistency in multi-client environments.
- Reliability of Real-Time Synchronization
The separation of notifications and data storage left room for ambiguity regarding the "true state" of the data.
The decision was made to redesign and reimplement the chat functionality as a standalone module, prioritizing the resolution of state consistency across multiple clients.
Design - Decision 1
Treat DB as the Single Source of Truth
Why: Client-driven state management can lead to inconsistencies.
How: Restrict all writes to RPCs (Database Functions). Treat client-side state solely as a "projection" of the database.
Design - Decision 2
Consolidate Authorization and Data Operations within the DB
Why: Authorization checks at the application layer can cause TOCTOU (Time-of-check to time-of-use) issues.
How: Use RPCs with INSERT ... SELECT to execute permission checks and writes in a single transaction. Implement Row Level Security (RLS) as the final line of defense.
Design - Decision 3
WAL-based Real-time Synchronization
Why: Notification-based systems (WebSocket events) decouple "storage" from "delivery," leading to potential inconsistencies.
How: Utilize Supabase Realtime's postgres_changes. Broadcast only "committed facts" triggered by the database Write-Ahead Log (WAL).
Design - Decision 4
End-to-End Type Safety
Why: To ensure safety during schema changes and maintain high refactoring resilience.
How: Adopt TypeScript. Generate types via Supabase CLI to share a unified schema across Client, Server, and Database.
Architecture
- Frontend: Next.js
- Backend (BFF): Express
- Database: Supabase (PostgreSQL + Realtime)
Separation of Responsibilities:
- Command (Write): HTTP / RPC
- Sync (State Synchronization): Realtime Subscription
This architecture ensures a clear separation between the processing path and the synchronization path.
Technical Challenge
During the redesign process, the following inconsistency was encountered:
Case: Authorization Inconsistency Across Multiple Tabs
1 User leaves an event in Tab A.
2 Immediately after, the user posts a message in Tab B.
3 While the post should be rejected, the operation succeeds.
Root Cause:
This issue stemmed from a sequential process where:
- The state was validated on the client side.
- The API call was executed only after that validation.
Solution
Approach: Full Closure within the DB Layer
- Convert message posting into an RPC (create_chat_message).
- Use INSERT ... SELECT to verify participant status simultaneously during the write operation.
- Complete the entire process within a single transaction.
Results:
- Eliminated the separation between permission verification and execution.
- Structurally resolved the TOCTOU issue.
Supplementary Strategy: Optimistic Updates and Rollbacks
- Immediate UI updates via Optimistic Update.
- Immediate rollback of state if the RPC is rejected.
Outcome
The redesign yielded the following improvements:
- Elimination of Invalid States
Posts that do not meet permission requirements are now structurally impossible.
- Simplified State Management
Clients focus solely on subscribing to the database state, reducing complex local logic.
- Guaranteed Eventual Consistency
States converge based on database facts, ensuring reliability even upon reconnection.
- Enhanced Reliability of Real-time Synchronization
Achieved a consistent model where only "committed data" is broadcast via WAL.
Summary
In this case, rather than a simple replacement of existing features, a "reconstruction focused on data integrity" was performed. Specifically, a chat system that remains robust in multi-client environments was realized through:
- Designing the DB as the Single Source of Truth (SSoT)**
- Unifying authorization and data operations via RPC**
- Implementing WAL-based real-time synchronization**
Through this redesign, the critical importance of determining at the architectural level exactly where integrity should be enforced was learned.