← Back to Project|Technical Documentation
Public · Read-only

38-table schema across 8 functional groups with design rationale

Database Design

The system contains 38 tables organized into 8 functional groups. All tables use integer primary keys except where noted. Timestamps (created_at, updated_at) are present on all tables.


Schema Overview

GroupTablesPurpose
Core Location2Geographic hierarchy (municipality → barangay)
Users & Authentication3Resident and official identity
Roles & Permissions5Spatie RBAC tables
Barangay Governance2Official terms and signing delegations
Housing & Households5Physical structures, economic units, member profiles
Document System4Transaction lifecycle and requirements
Document Detail Tables10Type-specific document fields
System & Infrastructure6Queue, notifications, Telescope

Core Location

municipalities

Top-level geographic unit. Links barangays to their city or municipality.

ColumnTypeNotes
idBIGINT PK
municity_codeVARCHAR(20)eGovPH standard code. Used for external API linking
nameVARCHAR(255)Unique
districtINTLegislative district number
zip_codeVARCHAR(4)

barangays

Primary jurisdictional unit. All residents, households, officials, and transactions are scoped to a barangay.

ColumnTypeNotes
idBIGINT PK
barangay_codeVARCHAR(20)eGovPH code. Retained for external API after migration
municity_codeVARCHAR(20) → municipalities.idInteger FK post-migration
province_codeVARCHAR(20)
region_codeVARCHAR(20)e.g., Region III – Central Luzon
nameVARCHAR(255)

Design note: The field is named municity_code but stores an integer FK after the barangay ID migration. The eGovPH code string is preserved separately for external API calls.


Users & Authentication

users (29 columns)

Central identity record for all residents and officials on the platform. Implements Filament multi-tenancy via barangay_id and municity_id. Supports soft deletes.

Key fields:

ColumnTypeNotes
idBIGINT PK
uuidVARCHAR(255)From eGovPH. Unique. Used for external API identity
family_idBIGINT → families.idNullable. NULL on family deletion
mother_id / father_idVARCHAR → users.idSelf-referencing. Nullable
barangay_idBIGINT → barangays.idPrimary jurisdictional link
municity_idBIGINT → municipalities.idMunicipality link
egov_dataJSONRaw eGovPH payload (IDs, PhilHealth, UMID, passport)
digital_signatureTEXTPath to official's signature image
deleted_atTIMESTAMPSoft delete

password_reset_tokens

Laravel built-in. email (PK), token (hashed), created_at.

personal_access_tokens

Laravel Sanctum. 10 columns. Polymorphic token ownership with abilities, last_used_at, expires_at.


Roles & Permissions (Spatie)

Standard Spatie Laravel Permission tables. Five tables total:

TablePurpose
permissionsGranular permissions, guard-scoped
rolesRole definitions (Captain, Secretary, Kagawad, Admin, Super Admin)
model_has_rolesPolymorphic role assignments
model_has_permissionsDirect per-model permission overrides
role_has_permissionsRole → permission mapping

Permission cache is invalidated on every role assignment or revocation via PermissionRegistrar::forgetCachedPermissions().


Barangay Governance

barangay_terms (8 columns)

One row = one official's tenure in one position at one barangay. The authorization middleware checks this table on every document signing attempt.

ColumnTypeNotes
idBIGINT PK
user_idBIGINT → users.id
barangay_idBIGINT → barangays.id
position_idBIGINT → roles.idRESTRICT on delete
started_atTIMESTAMPDefaults to current timestamp
ended_atTIMESTAMPNULL = currently active

On term creation, the user is automatically assigned the corresponding Spatie role.

delegations (6 columns)

Records a signing authority grant from a Captain to another official.

ColumnTypeNotes
idBIGINT PK
granter_term_idBIGINT → barangay_terms.idThe Captain's term
delegate_term_idBIGINT → barangay_terms.idThe delegate's term
expires_atTIMESTAMPNULL = no expiry

All delegations are automatically voided when the granting Captain's barangay_terms.ended_at is populated.


Housing & Households

houses (7 columns)

Physical dwelling unit. Foundational jurisdictional anchor — a household's barangay_id flows from its house.

ColumnTypeNotes
idBIGINT PK
barangay_idBIGINT → barangays.idEnforces jurisdictional locking
housing_unitVARCHAR(255)Unit/lot number
streetVARCHAR(255)
subdivisionVARCHAR(255)Nullable

households (9 columns)

Economic/social unit within a house. Multiple households can occupy one house (e.g., extended family + boarders).

ColumnTypeNotes
idBIGINT PK
house_idBIGINT → houses.idCASCADE on delete
household_head_idBIGINTRefs household_member_profiles.id. No FK constraint (circular dep)
ownershipVARCHAR(255)Owned, Rented, etc.
monthly_utility_expenseDECIMAL(10,2)
total_incomeDECIMAL(12,2)
expires_atTIMESTAMPHousehold dissolution timestamp. NULL = active

Design note: household_head_id has no database-level FK to avoid a circular dependency between households and household_member_profiles. Head assignment is managed at the application layer.

household_member_profiles (12 columns)

Junction table linking a user to a household with role, presence, and tenure metadata.

ColumnTypeNotes
idBIGINT PK
user_idBIGINT → users.idCASCADE
household_idBIGINT → households.idCASCADE
roleVARCHAR(255)Head, Spouse, Member
membership_typeVARCHAR(255)primary_resident, transient, associate
presence_statusVARCHAR(255)Present, OFW, Deceased
monthly_incomeDECIMAL(10,2)
started_atTIMESTAMP
ended_atTIMESTAMPNULL = currently active

Partial unique indexes:

-- Prevents double-dipping for social services
CREATE UNIQUE INDEX idx_unique_active_primary_resident
ON household_member_profile (user_id)
WHERE presence_status = 'Primary' AND end_date IS NULL;

-- Ensures each household has exactly one active Head
CREATE UNIQUE INDEX idx_single_active_household_head
ON household_member_profile (household_id)
WHERE role = 'Head' AND end_date IS NULL;

families (8 columns)

Nuclear family unit. Managed automatically by UserObserver. Linked to father_id, mother_id, barangay_id, and optionally a household_id.

residency_requests (16 columns)

Temporary holding record for pending residency applications. Drives the household linking workflow on official approval.

ColumnNotes
household_idNULL = request to create a new household. Populated = request to join existing
roleRequested household role (defaults to "Head")
membership_typeDefaults to "Primary"
statusPending / Approved / Rejected / Cancelled
approver_id → users.idOfficial who took action
actioned_atTimestamp of approval/rejection

Document System

document_type_properties (9 columns)

Master catalog of all supported document types.

ColumnNotes
codeShort unique code, e.g., BRGY_CLR
doc_type_modelPHP model class name for polymorphic detail routing
validity_daysDrives expiry_date calculation. NULL = no expiry
default_feeDefaults to 0.00

document_transactions (18 columns)

Central record for every document request or issuance. The full lifecycle is tracked here.

ColumnTypeNotes
idBIGINT PK
approver_idBIGINT → barangay_terms.idTerm record, not user ID (legally accurate audit)
on_behalf_ofBIGINT → barangay_terms.idSigning on behalf of (delegation)
document_type_idBIGINT → document_type_properties.id
signing_capacityVARCHARNative or Acting
statusVARCHARpending / issued / rejected
request_originVARCHARonline or walk-in
requester_idBIGINT → users.idNullable — NULL for walk-in requests
barangay_idBIGINT → barangays.idJurisdictional scope
checksumCHAR(64)SHA-256 hash for tamper detection. Unique
download_tokenVARCHAROne-time token. Refreshed on each download
file_pathVARCHARPath to generated PDF

Design decision: approver_id references barangay_terms.id rather than users.id. This creates a legally defensible audit trail — even years later, the system can answer exactly who was legally authorized to sign a document and in what capacity.

transaction_requirements (8 columns)

Stores actual supporting documents submitted per transaction.

ColumnNotes
transaction_id → document_transactions.idCASCADE
requirement_id → document_requirements_definitions.id
value_textText value (e.g., CTC number)
file_pathUploaded file path
is_verifiedBoolean. Defaults to FALSE

Document Detail Tables

Each document type has a dedicated satellite table linked 1-to-1 with document_transactions via transaction_id. These tables store only the fields unique to that type.

TableKey Fields
clearancescommunity_tax_id
business_clearancesbusiness_name, business_type, ownership, services, location
construction_clearanceslocation
tricycle_clearancesnew_owner_id, requested_for_id, purpose, body_number
jobseeker_certificates(no additional fields; all data from transaction)
guardianship_certificatesguardian_id, relationship, address_id → barangays
indigency_certificatesrequested_for, purpose
indigencysps_certificatesfather, mother, address_id → barangays
residency_certificatesrequested_for, length_of_residence
solo_parent_certificatessolo_parent_name, no_of_child, address_id → barangays

DocumentTransaction::getSpecificDetails() resolves the correct detail record dynamically using the doc_type_model field on document_type_properties.


System & Infrastructure

TablePurpose
notificationsLaravel built-in database notifications (UUID PK)
jobsLaravel Queue pending jobs
failed_jobsJobs that exhausted all retries (full stack trace stored)
telescope_entriesRequests, queries, jobs, exceptions (added Week 6)
telescope_entries_tagsTag-based filtering (e.g., Auth:1, App\Models\User:1)
telescope_monitoringActively monitored tags