This page offers a bird’s-eye view of the key architectural decisions in ProFed and the reasoning behind them. It is a starting point for developers who want to understand the codebase before contributing. More detailed documentation will live in the repository as the project matures.
Modular monolith
ProFed is structured as a modular monolith: internally divided into clearly separated components with defined boundaries, but deployable as a single application.
A small installation can run the core system with minimal setup. A larger installation can split those same components across separate services or containers. The architecture supports both without requiring different code for each case.
Some components run as separate processes by design – the Profile Importer and Job Scraper are examples – triggered by a build tool, a CMS, or a scheduler. The modular structure accommodates this naturally.
Event sourcing
ProFed uses event sourcing. Each component derives its current state from a sequence of historical events. Components maintain a projection of their state in a database for performance, but this projection is always derived data. When a component restarts, it discards the projection and rebuilds it by replaying events from snapshots and history.
No forced schema migrations
Many changes to the domain model do not require database migrations. Because state is derived, updating a component and replaying its events is often enough. JSON as the internal event format also makes model extensions simpler than altering table structures in a relational database.
Hot failover
Multiple instances of the same component can run in parallel, each maintaining consistent state by processing the same event stream independently. If one instance fails, the others continue without interruption.
Independent deployment
Each component manages its own state and can be stopped, updated, and restarted without coordinating with other components.
Event sourcing requires a different way of thinking for developers coming from classical systems. Eventual consistency must be reasoned about explicitly, and changing event structures is possible but a different process than updating a database schema.
Flexible message bus
Components communicate through an internal message bus. The current implementation is backed by PostgreSQL, but the interface is defined independently of the backing technology. In future, the PostgreSQL implementation can be replaced with a higher-throughput system without changing how components communicate with each other.
Search results as feeds
ProFed uses ActivityPub in a way that has no direct equivalent in the Fediverse today: search queries become subscribable Actors.
You follow a search Actor and results arrive in your timeline as regular ActivityPub content – the same way posts from people you follow do. You unfollow when the search is no longer relevant. Candidate search works the same way from the recruiting side.
The practical consequence is significant: federated search does not need to be fast. Results accumulate over time as instances across the network process the query, replacing the complexity of real-time federated search with federation mechanics that already work.
How the concepts connect
Every data source in ProFed – a profile imported from a personal website, a job scraped from an external listing, an ActivityPub message received from another instance – enters the system as events on the message bus. Components consume these events, maintain their own state, and expose the results as ActivityPub content. Search Actors receive that content through normal federation as it arrives.
The same path handles everything: external sources write events, components process them, and the network distributes the results. Whether components share a single process or run in separate containers does not affect this flow.
Further reading