Production2026

Leganews Pro

Building a structured legal monitoring platform for the Congolese legal market

Cover image for Leganews Pro

Leganews started in 2019 with a difficult but practical question: how can legal professionals in the Democratic Republic of Congo reliably find the legal texts they need when the information is scattered across official publications, institutional websites, scanned files, private archives, and documents shared from person to person?

As a co-founder, I have lived with this problem from both sides of the product. I have helped shape what Leganews should become, and I have worked on the technical system required to make that vision reliable. That has meant making product decisions, learning from legal professionals and editors, maintaining a real legacy application, and eventually accepting that some of our first abstractions were no longer strong enough for the platform we wanted to build.

The problem was not simply that legal information existed only on paper. The deeper problem was that digital access did not automatically make the law easy to find, verify, understand in context, or monitor over time.

Leganews evolved from a legal database into a legal monitoring platform built around a curated corpus, structured search, versioned legal texts, editorial review, legal relationships, alerts, document comparison, access entitlements, and billing.

This evolution did not happen in one clean rewrite. The legacy product proved that people needed searchable legal content, subscriptions, payments, favorites, search history, and an administration surface. It also exposed where our model was too generic, where responsibilities were coupled, and where adding new features created more exceptions than clarity. The current platform is the result of refactoring those lessons into a deeper domain model.

The goal is not to replace official publications or the judgment of a legal professional. The goal is to reduce the time and uncertainty between a legal question and the sources required to work on it responsibly.

Leganews combines domain modeling, editorial tooling, information retrieval, asynchronous processing, access control, payment integration, and product design around one central idea:

A legal text becomes truly useful when it can be found, verified, read in the correct version, connected to related texts, and monitored as it changes.

The Problem

The core problem behind Leganews is legal uncertainty caused by fragmented access to legal information.

In the Democratic Republic of Congo, a lawyer, company, researcher, magistrate, or student may need to move between the Journal officiel, institutional websites, search engines, PDF collections, physical archives, and documents received from colleagues before locating a useful text.

Even after finding a document, several questions remain:

  • Is this the correct legal text?
  • Is the title, number, date, and source accurate?
  • Is this the original version or a later version?
  • Has it been modified, completed, replaced, or repealed?
  • Is the document complete?
  • Is there a related decision or implementing text?
  • How can I find it again when I return to the same matter?
  • How will I know when something relevant changes?

A folder of PDFs solves storage, but it does not solve legal retrieval. A generic website search can find keywords, but it does not understand legal types, jurisdictions, classifications, subjects, versions, or relations. A simple database can list records, but it does not automatically provide an editorial process for deciding what becomes public.

In practice, legal research becomes repeated reconstruction work. Every professional creates a personal system of bookmarks, folders, filenames, notes, and memory. That system is difficult to verify, difficult to share, and difficult to maintain.

Leganews was built to turn that fragmented workflow into a structured path:

Mermaid

The corpus is intentionally described as curated rather than exhaustive. The presence of a text in Leganews does not, by itself, prove that the text is currently in force. The platform helps users inspect sources, dates, versions, and relations, but the final legal analysis remains a professional responsibility.

Product Direction

Leganews started with a direct product idea:

Build a structured digital database of Congolese legal texts.

That idea was useful, but it was not sufficient.

A professional legal platform had to do more than upload and display documents. It needed to:

  • model normative acts and case law without flattening both into generic files;
  • preserve the stable identity of a legal text while supporting several versions;
  • structure long texts into articles, sections, chapters, paragraphs, and annexes;
  • separate draft editorial work from public publication;
  • connect texts through legally meaningful relations;
  • provide full-text search with legal filters;
  • let users save texts in contextual folders and attach personal notes;
  • monitor domains, subjects, and individual texts;
  • notify users when a matching version is published;
  • compare two versions or uploaded documents;
  • support individual and organization access;
  • process local payment methods safely;
  • migrate years of legacy data without losing business meaning;
  • remain observable and testable as the product grows.

This changed the product from a content website into a legal information system.

The public application is the research and monitoring surface. The administration application is the editorial and operational surface. The Symfony API owns the business rules, while PostgreSQL, Redis, object storage, and Mercure support search, background work, files, and realtime updates.

From the Legacy Platform to Leganews 2.0

The first Leganews platform was not a throwaway prototype. It was a production Symfony application that already followed Domain-Driven Design, processed subscriptions and FlexPay transactions, managed users, published legal content, stored favorites and search history, generated PDFs, and provided public and administrative interfaces.

Its stack reflected the product at that stage:

Plain text
Symfony + Twig
Stimulus + Turbo + Symfony UX Live Components
Webpack Encore
MariaDB / MySQL
Doctrine ORM and Doctrine Messenger
Cron-driven maintenance jobs

The application was organized around ContentManagement, IdentityAndAccess, SubscriptionBilling, Marketing, and a SharedKernel. That structure helped us ship and learn. The problem was not that the legacy system had no architecture. The problem was that its concepts had become too broad for the next version of the product.

The legacy Content entity is a good example. One object contained the title, Markdown, HTML, excerpt, category, subcategory, publication date, online state, public-access flag, source, type, legal state, reference, favorite count, view count, and unique-view count.

That model was efficient for publishing one current article-like record. It became limiting when we needed to express a stable legal object with several versions, structured articles and chapters, editorial transitions, consolidation, citations, attachments, generated manifestations, and different access rules.

The same pressure appeared elsewhere:

  • Favorite could only connect one user to one content record, but legal research needed named folders, saved items, notes, and context.
  • SubscriptionBilling owned both commercial payment behavior and the access created by a purchase, making payment and entitlement rules harder to evolve independently.
  • IdentityAndAccess combined account identity with organization and subscription access concerns.
  • direct object references crossed context boundaries, so loading or changing one concept could pull another context's model into the same operation;
  • the public website and administration UI were delivered by the same Symfony, Twig, and Symfony UX runtime even though their workflows were becoming very different;
  • MariaDB stored the product well, but the next search and analytics requirements benefited from PostgreSQL full-text search, trigrams, range-friendly data, JSONB, and HyperLogLog.

The refactor was therefore not a framework upgrade. It was a change in the way we described the business.

Legacy modelCurrent modelWhy it changed
ContentWork, Expression, Expression Unit, ManifestationSeparate stable legal identity, semantic versions, structure, and renditions
Category and SubCategoryClassification, Subject, Work Type, Work Source, JurisdictionRepresent different legal classification dimensions explicitly
FavoriteBookmark, Bookmark Item, Work NotePreserve the context of legal research instead of a flat saved flag
Content views and countersMonitoring consultations and HLL projectionsSeparate user history from scalable aggregate read statistics
ContentManagementCorpus and MonitoringSeparate editorial legal truth from user discovery and follow-up activity
IdentityAndAccessIdentity and AccessSeparate who the user is from what the user or organization may access
SubscriptionBillingBilling and AccessSeparate money, orders, and provider events from entitlements and seats
One Symfony-rendered applicationSymfony API plus main and admin TanStack Start applicationsLet customer and operational workflows evolve independently
Browser/server session couplingSame-origin BFF with server-side Symfony tokensKeep access and refresh tokens out of browser storage
MariaDBPostgreSQLSupport richer search, projections, indexing, JSONB, and HLL analytics
Manual cron entriesSymfony Scheduler and MessengerKeep schedules in code and move slow work to observable queues

One of the most important improvements was reducing cross-context coupling. The current platform prefers identifiers, commands, queries, read-model contracts, and domain events over passing another context's aggregate directly into an entity. Billing does not mutate a subscription. It announces and fulfills a paid order through an explicit boundary. Corpus does not own a user's favorites. Monitoring owns bookmarks and references Works by identity.

The frontend refactor followed the same principle. Instead of letting templates, controllers, session behavior, and product UI grow together, the platform now has a backend API and two dedicated React applications. The main application owns public discovery and self-service workflows. The admin application owns editorial and operational workflows. Shared packages provide API contracts, authentication workflows, search, the legal reader, editor behavior, UI primitives, analytics, and utilities without forcing both applications into the same screen architecture.

This refactor also gave us an opportunity to replace implicit behavior with explicit lifecycles:

Mermaid

The legacy platform gave us the evidence. The current platform gives those lessons stronger names and boundaries.

System Architecture

Leganews is organized around seven business contexts:

  1. Corpus owns legal Works, Expressions, structured text, editorial review, attachments, and legal relations.
  2. Monitoring owns search, alerts, notifications, bookmarks, notes, consultations, and recent searches.
  3. Identity owns users, authentication, account lifecycle, federated identities, and security challenges.
  4. Access owns subscriptions, organizations, members, invitations, and direct Work access grants.
  5. Billing owns products, prices, orders, payment transactions, and provider events.
  6. Compare owns document and legal-version comparison workflows.
  7. Policy owns the platform's versioned terms and privacy documents.

The backend follows Domain-Driven Design and a layered architecture inside each bounded context:

Mermaid

Commands change state. Queries return purpose-built projections. Doctrine ORM persists aggregates, while Doctrine DBAL handles optimized searches, reports, tables, and cross-record read models.

Mermaid

This separation matters because legal publication, search, subscriptions, and payments evolve for different reasons. Keeping their language and rules separate makes the code easier to navigate and prevents one large generic model from owning everything.

The hardest design problem was deciding what the platform actually stores.

A naive model could use a table named documents with a title, a PDF, and a publication date. That works until the same legal act receives a new version, an editor structures its content, a later text modifies it, or users need to compare two states of the law.

Leganews instead distinguishes four connected concepts: the stable legal Work, its semantic Expressions, their structural Expression Units, and the technical Manifestations used for reading or download.

This distinction is central to the platform.

A Work may be a normative act or a case-law decision. It owns stable metadata such as its title, type, source, classification, subject, publication date, legal reference, access policy, and current derived editorial state.

An Expression represents a version of that Work. Expressions have a kind, version number, expression date, validity period, structure state, editorial state, and summary. The current expression kinds are original and consolidated.

An Expression Unit represents the internal structure of the version: body, preamble, book, part, title, chapter, section, article, paragraph, alinea, point, or annex. Units form a hierarchy through parent identifiers and materialized paths.

A Manifestation is the technical rendition presented to a reader, such as structured HTML, Markdown, plain text, or a generated PDF.

Mermaid

This model lets the platform keep one stable page for the legal object while exposing the exact version a user is reading. It also provides a foundation for validity periods, consolidation, comparisons, citations, and future improvements to temporal legal research.

Editorial Workflow

Legal information should not become public only because a record exists in the database.

Leganews uses an explicit editorial state machine for Expressions:

Mermaid

Editors can submit a structured Expression for review. Reviewers can approve it or request changes with a required comment. Approved content can then be published. Withdrawn or approved content can be reopened as a draft.

Publication is a business event, not a boolean update.

When an Expression becomes published, the platform can:

  • derive the public editorial state of the Work;
  • refresh the Work summary from the latest published Expression;
  • build a readable Manifestation;
  • project inline legal citations into Work relations;
  • update the search document;
  • generate comparisons with other published Expressions;
  • schedule notifications for matching user alerts.
Mermaid

An important detail is that the Work's public state is derived from its Expressions. Publishing a new draft is not required to keep an older published version visible, and creating a newer draft does not accidentally unpublish the Work.

The administration application supports this workflow with a legal-text editor and review controls. The editor stores an editing representation while the backend persists domain-level Expression Units separately. This keeps the user experience of editing distinct from the canonical structured model used for search, reading, export, and comparison.

Legal texts are not independent records.

One text may cite, amend, partially amend, repeal, partially repeal, implement, apply, ratify, interpret, consolidate, or replace another text. Treating these as generic hyperlinks would lose their legal meaning.

Leganews models them as Work Relations.

A confirmed relation records a source Work, a target Work, a relation type, and optional grounding in an Expression or Expression Unit. Some relations also change domain state. For example, applying a repeal relation to a normative act can update the target Work's status to repealed. A modifying relation can optionally initialize a consolidated Expression from the latest relevant content.

Inline references created by editors are projected into cites relations when an Expression is published. The system also models Work Relation Suggestions so an automated process, import rule, or AI system can propose a relation with evidence and confidence without silently changing legal facts.

This separation is deliberate:

Mermaid

Automation can help discover possible links. It should not be allowed to publish legal meaning without review.

Search and Information Retrieval

Once legal content is structured, the next challenge is retrieval.

Leganews does not search directly across every normalized table at request time. Published Work data is projected into a dedicated search_documents model containing the latest published Expression, title, headings, body, legal references, and filterable metadata.

PostgreSQL provides the first search engine.

The search layer uses:

  • French full-text configuration;
  • unaccent normalization;
  • weighted tsvector values for titles, headings, and body text;
  • GIN indexes for full-text retrieval;
  • trigram indexes for approximate fallback matching;
  • ranked results with title and body highlights;
  • filters for Work kind, type, source, jurisdiction, classification, subject, access, and date;
  • explicit title-only, body-only, and full-text scopes.

The first attempt uses PostgreSQL full-text search. If it finds no results, the engine falls back to trigram similarity. This helps with spelling differences, incomplete references, and queries that do not tokenize cleanly.

Mermaid

Search is located in the Monitoring context rather than inside Corpus because search is part of the user's ongoing research activity. That same context owns recent searches, suggestions, saved Works, notes, consultations, and alerts.

The frontend packages the experience into a reusable search module shared by the public and administrative surfaces. Users can start broadly, select a search scope, apply legal filters, inspect highlighted fragments, and retain enough context to verify the result instead of treating a keyword match as a legal conclusion.

Monitoring, Alerts, and Personal Research

Finding a text once is only one part of legal work. Professionals also need to return to a matter and follow it over time.

Leganews models monitoring through several focused capabilities:

Mermaid

Bookmarks are more useful than a flat favorites list because they preserve context. A user can organize Works by client, research question, litigation file, or monitoring subject. Personal notes stay attached to the Work so the reason for saving it is not lost.

Alerts can target a whole legal classification, one subject, or one specific Work. When an Expression is published, an asynchronous handler finds matching subscribed users in bounded batches. It deduplicates notifications, applies access policies, stores an unread notification, publishes a realtime Mercure event, and sends email when that channel is enabled.

The platform also supports a weekly monitoring digest for users who prefer a consolidated summary.

Mermaid

This turns publication into a product feedback loop. The editorial team adds or updates a legal source; the system makes it searchable; users following the relevant scope are informed without repeating the same search every day.

Document and Version Comparison

Legal work often involves comparing two versions of a document line by line.

Leganews supports two related comparison workflows:

  1. compare two published Expressions of the same Work;
  2. compare two private PDF or DOCX files uploaded by a subscribed user.

The uploaded-document workflow accepts two files of up to 10 MB each. Processing runs asynchronously because extraction and diff generation should not block the HTTP request.

For PDF files, the platform extracts UTF-8 text with pdftotext. For DOCX files, it reads the WordprocessingML document directly from the ZIP archive and preserves paragraph and table separation. The text is normalized before the operating system's unified diff algorithm produces additions, deletions, and contextual hunks.

The comparison aggregate owns a clear lifecycle:

Mermaid

Temporary uploads are removed after processing. Results are retained for seven days and deleted by a scheduled cleanup. The user receives realtime status through Mercure and an email when processing completes or fails.

The implementation also handles operational edge cases: identical checksums, empty extracted text, tracked changes in DOCX files, oversized diffs, unsupported files, and safe failure reasons that do not leak internal details.

Access and Billing

Legal content access is a separate domain from payment.

This distinction is important. A payment is a commercial event. Access is an entitlement decision.

Leganews supports two access paths:

Mermaid

When a user requests a protected Work, the access authorizer first checks for an active direct grant. If none exists, it evaluates subscription access, including organization coverage and privileged roles.

Billing owns products, prices, orders, order items, payment transactions, and provider events. Access owns subscriptions, organizations, memberships, invitations, seats, and Work grants. The two contexts collaborate through explicit fulfillment services and events.

The checkout flow supports local payment needs through FlexPay, including mobile money and card payments in CDF or USD.

Mermaid

Payment callbacks are designed to be idempotent. The system computes a stable fingerprint for each provider event, ignores exact replays, locks the payment transaction while applying the result, and does not fulfill an order twice. Fulfillment validates every order item before creating any entitlement and marks the order fulfilled only after all items succeed.

This is one of the places where domain separation provides practical safety. Billing can say that money was collected. Access decides what that purchase is allowed to unlock.

API and Web Applications

Leganews has two React applications built with TanStack Start:

Mermaid

Both applications use React, TanStack Router, TanStack Query, Tailwind CSS, Zod, and shared workspace packages managed with Bun and Turborepo.

The browser does not store Symfony access and refresh tokens directly. Each TanStack Start application owns a same-origin backend-for-frontend session boundary. After authentication, tokens are stored server-side behind an HTTP-only session cookie. Browser requests go to the application's /api route, which forwards them to Symfony, adds the correct access token, refreshes an expired session when possible, and retries the protected request.

The API also receives an X-Client-Platform header. Symfony uses controller metadata and a centralized platform guard to prevent the main application from calling administrative surfaces or the admin application from accidentally using an unsupported route.

On the TypeScript side, @workspace/api exposes platform-specific clients:

Mermaid

Contracts are defined with Zod and TypeScript types are inferred from those schemas. The package hides raw URL construction, HTTP details, and query helpers behind small product-facing interfaces.

This creates a useful boundary:

Mermaid

The frontend can focus on user workflows. Symfony remains responsible for business decisions.

Migrating the Legacy Platform

Leganews is not a greenfield product. The initiative started years before the current architecture, and the new platform has to preserve useful data from the previous application.

The development environment runs both PostgreSQL and the legacy MariaDB database. A plan-based importer moves records in dependency order:

Mermaid

Each import plan maps one legacy concept to the new domain model, detects conflicts, supports dry runs and filters, and records whether each row was inserted, skipped, rejected, or failed.

Imports run in bounded batches so a large historical dataset does not have to fit in memory. The reset workflow can rebuild the PostgreSQL database, import legacy and seed data, rebuild search projections, reupload attachments to object storage, reconcile citation relations, and synchronize expired subscriptions.

The migration is not treated as a one-time SQL copy. The old schema and the new domain model do not express the same concepts. Mapping through explicit plans makes those differences visible and lets the new domain rules remain authoritative.

This was an important lesson: preserving data does not mean preserving the old model.

Applied AI with Editorial Boundaries

Leganews uses AI in a deliberately narrow role.

When a published Expression has no editorial summary, the platform can generate a short French summary through a configured OpenAI or Gemini model. The prompt requires a factual summary without interpretation or information absent from the source. Input size is bounded, provider failures are logged, and the summary stores its provenance as manual, generated, or extracted.

If no provider is configured or generation fails, the publication workflow can fall back to a deterministic summary extracted from the Expression Units. Publication does not need to become unavailable because an external model is unavailable.

The same principle appears in legal relations. Automated systems may create relation suggestions with evidence and confidence, but a suggestion remains different from a confirmed legal relation.

The design rule is:

AI may accelerate editorial work, but it should not silently become the source of legal truth.

Reliability, Observability, and Testing

A platform that depends on background jobs, payments, external storage, realtime delivery, and editorial workflows needs more than successful HTTP responses.

Leganews uses Redis-backed Symfony Messenger for asynchronous work and failed-message storage. Scheduled jobs handle tasks such as expired subscriptions, comparison cleanup, monitoring digests, account cleanup, and monthly platform reports.

Operational signals are separated by purpose:

  • structured rotating application logs;
  • business audit logs;
  • Sentry-compatible error capture;
  • Telegram delivery for business-critical failures;
  • correlation identifiers across buses and requests;
  • health checks for operational dashboards;
  • Google Analytics events behind privacy sanitization in the web applications.

Work consultation statistics use HyperLogLog for approximate unique-reader counts. This keeps aggregate read metrics useful without retaining a detailed anonymous history row for every visit.

Testing is also divided by responsibility:

Mermaid

The Behat scenarios are especially valuable because they describe application behavior in product language: a published Expression makes a Work readable, a duplicate payment webhook is processed once, an expired subscriber receives no later alert, and an organization with a running subscription cannot be archived.

That makes the tests more than a regression suite. They are executable documentation for the domain.

Key Engineering Decisions

The platform does not treat a PDF as the legal object. Work, Expression, Unit, and Manifestation separate stable identity, semantic version, structure, and technical rendition.

That model creates room for versioning, consolidation, search, citation, comparison, and future temporal queries.

Make publication an event

Publishing an Expression is the point where editorial work becomes product behavior. Search indexing, summaries, manifestations, citations, comparisons, and alerts all react to that transition.

This keeps the editor focused on editorial work while downstream capabilities stay decoupled.

An AI-generated or imported relation suggestion is not a confirmed Work Relation. Evidence, confidence, and editorial validation provide an explicit boundary between assistance and authority.

Use PostgreSQL before adding a separate search cluster

PostgreSQL already provides French full-text search, ranking, highlights, trigrams, relational filters, and indexes. A separate search service would add deployment and synchronization cost before the product has proved that PostgreSQL is insufficient.

Separate billing from access

Payment success does not directly mean “show the content.” Billing records commercial truth; Access grants product entitlements. Idempotent fulfillment connects them.

Keep tokens out of browser storage

The TanStack Start BFF owns each application session, forwards same-origin requests, and refreshes Symfony tokens server-side. The main and admin sessions remain separate.

Treat legacy migration as domain translation

The previous MariaDB schema is a source, not the target model. Ordered import plans translate old records into the current domain and report conflicts explicitly.

Use asynchronous processing where the user should not wait

Document extraction, comparisons, alerts, email, and other background workflows run through Messenger. Mercure closes the feedback loop by updating the interface when processing changes state.

Use AI as a replaceable adapter

Summary generation sits behind an application interface, supports more than one provider, stores provenance, and has a deterministic fallback. The core corpus does not depend on one AI vendor.

What I Learned

The biggest lesson from Leganews is that access to legal information is not solved by uploading more files.

Being a co-founder changed how I approached the refactor. I could not evaluate the system only as an engineer. Every architectural change had to protect years of editorial work, existing users, commercial rules, and the trust attached to the Leganews name. The best design was not the cleanest model in isolation; it was the model that could move the product forward without pretending its history did not exist.

The difficult part is preserving context.

A legal professional needs to know what the text is, where it came from, which version is being read, how it is structured, what it changes, what changes it, and whether the source is sufficient for the decision being made.

I also learned that editorial workflow is part of system architecture. In a legal product, data quality cannot be separated from how content is created, reviewed, corrected, approved, published, and withdrawn. A strong public search experience depends on a strong private editorial process.

Search became more important than expected. Once the corpus contained structured content, the next challenge was not storage but retrieval: French language normalization, ranking, fuzzy fallback, filters, highlights, and enough metadata for users to judge a result.

Monitoring changed the product direction as well. Legal research is rarely a single query. It is an ongoing activity. Bookmarks, notes, recent searches, alerts, realtime notifications, and weekly digests turn the corpus into a workspace rather than a static library.

The migration from the legacy platform reinforced another lesson: a rewrite should preserve business history without preserving every old abstraction. Explicit import plans made it possible to translate years of data into a clearer domain model while recording what could not be mapped safely.

Payments taught the importance of idempotency. Providers retry callbacks, users repeat actions, and distributed workflows can fail between steps. Stable event fingerprints, record locks, preflight validation, and idempotent fulfillment are what make the commercial flow trustworthy.

Finally, AI is most useful when its authority is limited. Generated summaries and relation suggestions can reduce editorial effort, but provenance, deterministic fallbacks, and human validation are more important than making AI the center of the architecture.

Conclusion

Leganews Pro is a legal information and monitoring platform for the Congolese legal market.

For me, as a co-founder, it is also a long-term exercise in turning a real institutional problem into a product and then having the discipline to redesign that product when its first model reaches its limits.

Its main engineering value is not that it stores laws and decisions. The value is the system around those materials: a domain model for legal identity and versions, structured editorial workflows, meaningful legal relations, indexed retrieval, personal monitoring, asynchronous comparison, safe access fulfillment, legacy migration, and carefully bounded AI assistance.

The platform turns scattered legal material into a curated workflow where a text can be registered, structured, reviewed, published, searched, read, cited, compared, saved, and monitored.

That is the idea behind Leganews: reduce the distance between a legal question and the reliable sources needed to answer it.

More projects

View all

2025

Basango

Towards a scalable and intelligent system for Congolese news curation

View

2025

PHP Packages Graph

Exploring the php ecosystem through packages published on packagist.org

View