Deep dive into the technical architecture of Church Ledger.
Church Ledger is designed as a monolithic Laravel application with a tightly coupled React frontend via Inertia.js. This architecture ensures maximum developer productivity while providing a smooth, SPA-like user experience.
The system revolves around four primary domains: Members, Transactions, Groups, and Audit Logs.
Stores demographic and spiritual data.
members: Central table with personal info, baptism status, and household foreign keys.households: Groups members for family-based giving and communication.Designed for double-entry integrity (conceptually).
funds: Categories for giving (e.g., General, Building, Missions).contributions: Tracks individual tithes/offerings with high-precision decimals (decimal(19, 4)).reconciliations: Batch records of processed giving.Every state change is tracked.
activities: Stores the 'who', 'what', 'when', and 'before/after' snapshots of model data.Auditable TraitMost models in Church Ledger implement an Auditable trait that hooks into Eloquent events.
trait Auditable
{
protected static function bootAuditable()
{
static::updated(function ($model) {
ActivityLog::create([
'user_id' => auth()->id(),
'subject_id' => $model->id,
'subject_type' => get_class($model),
'action' => 'update',
'payload' => [
'before' => $model->getOriginal(),
'after' => $model->getAttributes(),
],
]);
});
}
}We use Spatie Laravel Permission under the hood, but wrap it in custom Gate definitions to handle complex ministry scenarios (e.g., a Deacon can see attendance but not giving records).
Gate::define('access-giving', function (User $user) {
return $user->hasRole('Admin') || $user->hasPermissionTo('view-finances');
});Privacy is paramount given the sensitive nature of church data.
with() to prevent N+1 query issues in member lists.(member_id, date) for contribution queries and (church_id, slug) for multi-tenant isolation.