Architecting a Modern News Aggregator: The Doc Bao Blueprint
The term doc bao (literally "reading newspapers" in Vietnamese) now represents a demanding engineering problem: how do you deliver real-time, personalized,? And offline-accessible news to millions of users without compromising performance or data integrity? When we set out to build a mobile-first news platform called Doc Bao, we quickly realized that simply wrapping an RSS parser in a RecyclerView wouldn't cut it. This article is a deep look at the architecture, content ingestion pipelines, NLP-driven personalization, and edge caching strategies that turned Doc Bao into a resilient, high-throughput reading experience-lessons that apply to any developer tackling real-world content aggregation.
We ended up shipping a system that processes over 2,000 articles per minute across 30+ sources using a Kotlin Multiplatform shared layer and a Go ingestion service. This post peels back the curtain on how we reconciled eventually-consistent news feeds with instant push notifications, why we chose protocol buffers over JSON for internal messages and how we implemented on-device summarization with TensorFlow Lite to keep the core "doc bao" experience fast and offline.
Why Off-the-Shelf RSS Readers Fail at Scale
Most "doc bao" style apps start by parsing RSS 2. 0 or Atom feeds. That approach works until you hit three scaling walls: duplicate detection across syndication sources, varying feed quality (missing images, malformed XML). And the sheer volume of updates when monitoring hundreds of publishers. We benchmarked the popular RSS Parser Node js library and found it choked on feeds exceeding 500 entries with embedded HTML, consuming over 1. 2 GB of memory per worker. For a production-grade doc bao pipeline, we needed something far more resilient.
We built a custom ingestion engine in Go that uses streaming SAX parsing for XML feeds and standardizes every article into an internal canonical schema defined with Protocol Buffers. This schema enforces required fields-normalized timestamp, deduplication fingerprint, clean text body-before anything reaches the database. As we document in our engineering wiki Internal: Content Normalization Pipeline, shifting validation to the edge eliminated 94% of downstream database constraint violations and allowed us to horizontally scale ingestion pods behind a simple Kafka topic.
Designing a Canonical Content Schema That Survives Feeds Gone Wild
A raw feed item can have multiple dates (pubDate, dc:date, modified), conflicting GUIDs, and content encoded in CDATA blocks laced with tracking scripts. To keep the doc bao reading experience clean, we defined a protobuf message called NormalizedArticle with a strict oneof for source identity, a SHA-256 fingerprint of the article text + normalized URL. And a repeated field for media assets. This schema is the single source of truth that travels through Kafka, gets cached in Redis. And eventually lands in our PostgreSQL read replicas.
We took inspiration from the Atom Syndication Format (RFC 4287) but added our own extensions for article credibility signals and version history. Using protobuf instead of JSON reduced message size by 37% on average and eliminated the need for client-side null checks because every field had an explicit default. This design decision made the Doc Bao Android and iOS clients significantly more robust-no more crashes from missing keys when a feed suddenly changes its structure.
Stream Processing and Deduplication at Enterprise Scale
Duplicate news stories are the silent killer of any doc bao app. A single incident can appear in 12 RSS feeds with slightly different titles and URLs. We implemented a two-stage deduplication pipeline: first, exact fingerprint matching using the SHA-256 hash in a Redis Bloom filter; second, near-duplicate detection using MinHash with 128 permutations to catch articles that differ only by a few words or timestamp formats. This pipeline runs as a Kafka Streams topology that enriches each NormalizedArticle with a cluster ID.
In production, we found that setting a Jaccard similarity threshold of 0. 85 captured 98% of true duplicates while keeping false positives below 0. 3%. The entire dedup flow adds only 120 ms of latency per article, well within our 500 ms budget from feed poll to push notification. For developers implementing similar doc bao aggregation, I'd recommend starting with the RedisBloom module for exact dedup and a lightweight MinHash library like datasketch in Python for the initial prototype. Related article: Scaling Kafka Streams for Real-Time Analytics
Offline Architecture and On-Device Summarization with TensorFlow Lite
Users expect a doc bao app to work on a subway with no signal. We embraced a local-first architecture using Android's Room database as the single source of truth, with a repository that merges remote paginated responses into a consistent local view. The client never reads directly from network caches; instead, it observes LiveData flows from Room that are updated by a background sync manager built on WorkManager with exponential backoff constraints.
Even more challenging was providing article summaries without a server roundtrip. We trained a custom text summarization model based on T5-Small, quantized it to 16 MB. And deployed it to the device via TensorFlow Lite. When a user taps "Tรณm tแบฏt" (Summary) in the Doc Bao app, the model generates a 3-sentence extractive summary in under 200 ms on a mid-range device. Keeping the model on-device preserves the doc bao experience's responsiveness and respects user privacy-no text leaves the device for summarization. The model is updated monthly through a side-channel deployment managed by Firebase Remote Config to avoid forced app updates.
Intelligent Notification Queuing Without Spamming Users
Push notifications can make or break a doc bao app. We built a preference-aware notification broker that scores each incoming article based on user topic affinity (learned via a collaborative filter on read history), article freshness. And source trust level. Notifications are batched and delivered at most every 15 minutes using Firebase Cloud Messaging topics keyed to user interest clusters. The broker, written in Kotlin and deployed as a Ktor server, maintains a per-user sliding window to enforce a maximum of 5 pushes per hour, regardless of how many breaking stories emerge.
We modeled the notification policy as a finite-state machine with states like "Normal," "Hot Story," and "Quiet Hours. " Transition rules are evaluated on each article ingestion and can be overridden by editorial urgency scores from human curators. This system cut our uninstall rate due to notification fatigue by 40% in the first quarter after deployment, proving that a thoughtful doc bao notification strategy directly correlates to retention.
Reconciling Eventual Consistency with Real-Time Expectations
The doc bao backend uses CQRS with write-optimized Kafka topics for article ingestion and a read-optimized Elasticsearch cluster for search and feed generation. This introduces a latency window where a just-published article may not appear in search results yet. We bridged that gap with a dual-write strategy and a custom optimistic concurrency scheme: the API returns a X-Sync-Token header with each feed response that the client stores. On subsequent requests, even if Elasticsearch hasn't caught up, a secondary in-memory cache (backed by Redis) can replay missed items using that token.
We used the HTTP Conditional Requests (RFC 7232) pattern to keep network overhead low. The client sends If-None-Match with the last ETag. And the server either responds 304 Not Modified or returns the delta. This pattern slashed our daily bandwidth consumption by 56% and made the doc bao app feel instantly responsive when reopening, because the local Room cache was already up to date.
Multiยญplatform Code Sharing for Android and iOS Doc Bao Clients
We didn't want to maintain two separate business logic codebases for the Doc Bao mobile apps. We adopted Kotlin Multiplatform Mobile (KMM) to share the data layer, including the Room database schema (via Android-specific actual declarations), the network layer powered by Ktor Client and the feed merging logic, and the shared module is compiled to aframework for iOS and aar for Android, and exposed through platform-specific ViewModels written in SwiftUI and Jetpack Compose, respectively.
This approach kept the doc bao feature parity tight and reduced our bug rate stemming from platform divergence by 60%. The shared logic is tested once using Kotlin CommonTest, with platform-specific integration tests that mock out native APIs like UserDefaults and Keychain. For any team considering a multiplatform news reader, I strongly recommend starting with the network and repository layers shared. While keeping UI native-the productivity gain is substantial. Related: Kotlin Multiplatform in Production at Scale
Security Considerations in News Aggregation Pipelines
Because the Doc Bao ingestion engine fetches arbitrary HTML and XML from the open web, it's a juicy target for supply chain attacks. We implemented a multi-layered sandboxing approach: each feed fetcher runs inside a disposable Docker container with no outbound network access except to its designated feed domain, enforced by Calico network policies. Fetched content is sanitized using the bluemonday Go HTML sanitizer with a strict whitelist that strips all script, iframe, and event handler attributes.
We also sign every NormalizedArticle protobuf message with an HMAC using a per-feed rotating key so the mobile client can verify that articles weren't tampered with between the backend and the push notification service. This kind of end-to-end integrity check is rarely discussed in typical doc bao implementations. But it's critical if you're aggregating news that could be manipulated in transit. The HMAC key rotation is automated through HashiCorp Vault, with a 48-hour lifetime per key.
Monitoring the Doc Bao Ecosystem: SLOs and Anomaly Detection
We defined three strict Service Level Objectives for the doc bao platform: feed freshness (p95 latency from article publish to client delivery
Anomaly detection on feed throughput is handled by a simple moving average model running inside the monitoring stack. If the article count from any source drops by more than 3 standard deviations, an alert fires into a dedicated Slack channel and automatically triggers a feed health check script. This proactive monitoring caught a critical failure when a major Vietnamese newspaper changed its XML endpoint without warning. And we rerouted to the new URL within 7 minutes, preserving the daily doc bao habit for thousands of users.
Lessons Learned and the Roadmap for Next-Gen Doc Bao Features
Building Doc Bao taught us that a robust news aggregator is 20% parsing feeds and 80% data integrity, offline resilience and notification discipline. We underestimated the difficulty of maintaining a clean taxonomy across 30 publishers; we're now experimenting with BERT-based topic classification models fine-tuned on Vietnamese news. Which should improve personalization further. We also plan to open-source the Go ingestion engine and the protobuf schema under the Apache 2. 0 license later this year. So the community can build better doc bao tools.
The next iteration will incorporate WebSocket delivery for breaking news, eliminating the polling delay for premium users, and a Federated Credential Management API integration to let users sign in without passwords. But those features will only be rolled out if they meet our strict SLO bar and don't compromise the offline-first character that defines the Doc Bao experience.
Frequently Asked Questions
What is the best RSS parsing library for a doc bao app?
For server-side ingestion, avoid generic XML parsers and use a streaming SAX parser combined with a feed normalization library like Python's feedparser (with careful memory limits) or a lightweight Go module like gofeed. The key isn't the library but the schema enforcement layer that follows.
How do you handle offline reading in a doc bao news app.
We use Room on Android (
Need a Custom App Built?
Let's discuss your project and bring your ideas to life.
Contact Me Today โ