| Author: Abdullah Ahmed | Category: Custom Web Application Development
A customer changes their address, and an old invoice suddenly displays the new location. The database has stored a relationship correctly from a technical perspective, but the model has confused a current profile with a historical business fact. Database design decisions shape what the application can remember, explain, and protect.
For a custom web application, the database should reflect the business concepts and rules the software must support. Choosing a database engine is only one part of that work. Relationships, constraints, transaction boundaries, access, migration, and recovery all influence the reliability and cost of the finished service.
Model the business before the screens
Begin with the things the organisation needs to track and the events that change them. Orders, customers, approvals, subscriptions, and invoices have meanings that should remain clear even when the interface changes. A screen can combine several concepts, so copying its fields directly into one table may create an awkward long-term model.
Ask domain experts to walk through ordinary and exceptional cases. Can an order exist before a customer account is created? Can one invoice cover several orders? Can an employee belong to more than one organisation? These questions expose relationships that are easy to miss in an early wireframe.
Use a small glossary to distinguish similar terms. A request, a reservation, and a confirmed booking may look alike but have different rules. Agreeing on their meaning reduces ambiguity across database names, APIs, reports, and support conversations.
Decide what must remain true
Identify invariants: conditions that should hold regardless of which application path changes the data. Examples include a unique external reference within an account, a required relationship to an owning organisation, or a quantity that cannot be negative under the business model.
The PostgreSQL constraints documentation explains mechanisms such as primary keys, foreign keys, uniqueness, and checks. Equivalent capabilities vary across database systems, so choose the appropriate enforcement mechanism for the actual engine and rule.
Application validation remains useful for clear feedback, but durable rules should also be protected where the data is stored when practical. Multiple services, imports, administrative tools, and concurrent requests may otherwise bypass a rule implemented in only one interface. Translate constraint failures into understandable application outcomes without exposing internal details.
Use identifiers with a clear purpose
Separate the stable identity of a record from a label people may change. An email address, company name, or product code can have business meaning and still be unsuitable as the only internal identity. Understand whether those values can be corrected, reused, or scoped differently across organisations.
Choose identifiers with regard to creation, distribution, indexing, and exposure. Sequential integers and generated identifiers have different operational characteristics; neither is universally correct. Avoid treating an unpredictable identifier as a substitute for authorisation.
Document external references separately. An integration may supply an identifier that is unique only within a provider account. Preserve the provider and account context needed to interpret it. A single unqualified external ID field can create collisions or confusing lookups as integrations expand.
Represent relationships explicitly
Use relationships that reflect the business's actual cardinality. One customer may have several addresses, one user may belong to several organisations, and one product may appear in many orders. A design that stores a comma-separated list in a text field can make validation, querying, and updates unnecessarily difficult.
Consider the attributes of the relationship itself. Organisation membership may include a role, start date, and status. An order line may include quantity and the agreed price. These are often meaningful records rather than incidental links.
Define what happens when related data is removed or changed. Automatic cascading deletion may be appropriate for temporary child records but dangerous for historical transactions. Make deletion behaviour a business decision and test it with realistic examples before relying on default framework conventions.
Distinguish current state from historical facts
Some values should follow the current record; others must preserve what was true at the time of an event. A customer's current contact number may belong in a profile. The agreed description, price, and delivery details on an order may need a historical snapshot.
Decide which changes require an audit trail and what that trail must explain. Recording only the latest status may be insufficient when staff need to understand who approved a request and under which conditions. Conversely, storing every technical change indefinitely can create unnecessary complexity and data exposure.
Choose a history model proportionate to the need. Explicit event records, revision tables, or targeted audit entries may be enough. Do not adopt an elaborate event-sourcing architecture merely because some history is required. The model should support the questions the business and operators actually need to answer.
Choose data types for meaning and precision
Money, dates, quantities, and identifiers need deliberate representation. Define currency, units, precision, and rounding rules before deciding storage types. A value that appears as a decimal in the interface may have a more precise business representation internally.
Distinguish a calendar date from an instant in time. A service date, an expiry instant, and a local opening time are different concepts. Record the time-zone context required to interpret them and avoid relying on the server's local configuration as an unstated business rule.
Explain the meaning of missing values. Unknown, not applicable, not yet supplied, and intentionally cleared may require different treatment. A blanket default such as an empty string or zero can conceal those distinctions and complicate reporting later.
Design transactions around business consistency
Identify changes that must succeed or fail together. Creating an order and its lines may belong in one transaction. Updating a local record and charging an external payment service cannot usually be treated as a simple local database transaction with identical guarantees.
Keep transaction boundaries clear and avoid holding database resources while waiting unnecessarily for external services. For cross-system work, design explicit states, duplicate protection, and reconciliation. The database should help the application represent pending and uncertain outcomes rather than forcing everything into a premature success flag.
Test concurrent actions against important rules. Two requests may both observe available capacity before either commits a booking. The appropriate solution may involve constraints, locking, isolation choices, or a different operation design. Verify behaviour under concurrency rather than assuming a sequence that works in a single-user test will remain correct.
Plan queries around real access patterns
List the important reads and writes: a customer's recent orders, an employee's approval queue, an export for finance, or a lookup by external reference. Estimate plausible data volumes and relationships. This gives indexing and query design a concrete basis.
Use query plans and representative data when investigating performance. An index can improve a read while adding storage and write overhead. The order of columns and the query's filtering and sorting behaviour matter. Avoid creating an index for every field without evidence of its use.
Watch for application patterns that generate many repeated queries. A listing page may fetch related records separately for every row. Address the retrieval pattern before assuming the database needs more hardware. Measure the complete request and verify that improvements preserve the required data and access rules.
Balance normalisation and deliberate duplication
Normalisation can reduce inconsistent copies of facts by giving each concept a clear home. It is a useful starting point for many transactional applications. However, some duplication is intentional, such as historical snapshots or derived read models used for a specific workload.
When duplicating data, name the authoritative source and the update mechanism. Explain how stale or inconsistent copies are detected and repaired. A fast reporting table becomes a reliability problem if nobody can establish whether it matches the underlying transactions.
Keep derived structures replaceable where practical. Document how they can be rebuilt and test that process with realistic data. This reduces dependence on a one-time migration or an undocumented sequence of manual corrections.
Protect tenant and user boundaries
For a multi-tenant application, decide how records are associated with organisations and how that association is enforced. Shared tables, separate schemas, and separate databases involve different isolation and operating trade-offs. Choose based on actual access, scale, and ownership requirements.
Include tenant context in relationships and uniqueness rules where the business requires it. A query that filters the main record correctly can still expose another tenant's related data if the model permits an invalid association. Test both ordinary access and attempted cross-boundary relationships.
Limit database access for application and administrative roles. Use protected credential management and separate environments appropriately. Keep sensitive data out of routine development copies unless there is an approved, necessary process with suitable safeguards.
Treat schema changes as a deployment problem
The application and database may not change at exactly the same instant. Plan migrations so deployed versions can operate safely during the transition. Adding a new field, populating existing records, switching reads, and removing an old field may need separate steps.
Estimate migration work using realistic data size and database behaviour. A change that is quick in development may hold locks or consume substantial resources in production. Rehearse consequential migrations and define monitoring, interruption, and recovery procedures.
Keep migration scripts in version control and make their expected state clear. Avoid manual production edits that leave the recorded schema history inaccurate. If an emergency correction is necessary, reconcile it with the maintained migration process afterward.
Build recovery into the design
Define what data loss and service interruption the business can tolerate, then choose backup and recovery arrangements accordingly. Include the application, configuration, and external dependencies needed to make restored data usable. A backup file is not evidence of a working recovery process.
Practise restoration in an isolated environment. Verify important records, relationships, and application workflows after recovery. If external systems continued processing while the application was unavailable, plan how their state will be reconciled with the restored database.
Assign operational ownership for backups, access review, capacity, and maintenance. A custom application needs an ongoing data service, not only a schema delivered at launch. Document the routine tasks and the signs that require investigation.
Walk through a purchase record over time
Imagine a business application that records equipment purchases. At creation, a purchase has a requester, an owning organisation, one or more lines, and a draft status. The model should explain whether a line references a current catalogue item, preserves an agreed description, or does both. This distinction becomes important when the catalogue changes after approval.
Next, the requester changes department before the purchase is approved. Decide whether the purchase remains associated with the original budget or follows the person's current department. Neither answer should emerge accidentally from a database join. Record the relevant business fact at the point the organisation intends it to become fixed.
Now consider an approval limit that changes during review. The application may evaluate current authority at the time of approval while preserving the rule context needed to explain the decision later. Work with the business owner to determine the necessary history. A generic audit timestamp may not answer which policy applied.
Finally, import the approved purchase into a finance system and receive a delayed confirmation. Store the external reference and integration state without replacing the purchase's own identity. If the same confirmation arrives again, it should be associated with the existing operation. The data model should make this relationship explicit enough to support reconciliation.
This walkthrough can produce a compact set of schema and behaviour checks. The agreed description remains stable after a catalogue edit; a department change follows the chosen policy; an approval is attributable; and duplicate integration messages do not create duplicate purchases. These checks connect database design to evidence a reviewer can understand.
Budget for data administration and reporting
Reporting needs often arrive late and reveal that the application cannot distinguish important states or dates. Discuss a few representative operational questions during design: which requests are awaiting a decision, how long completed work took, and which external records remain unmatched. These questions can identify missing facts without requiring a large reporting platform at launch.
Keep reporting calculations aligned with the source data's meaning. A status-update timestamp may not represent the start of a business stage if unrelated edits also change it. If elapsed time matters, store the events needed to calculate it accurately. Avoid retroactively inventing history from a current-state field.
Provide supported correction paths for ordinary data problems. Duplicate customer records, invalid imports, and mistaken assignments are foreseeable operating cases. Administrative tools should enforce the same important rules as the main application and record consequential changes. Direct database access should not be the routine interface for business corrections.
Consider data export and eventual migration. The organisation should understand how to retrieve its records with their relationships and meanings intact. An export containing only human-readable labels may lose stable identifiers; an export of raw tables may be difficult to interpret without documentation. Define an appropriate ownership and portability approach for the service.
Include these responsibilities in the operating estimate. Database design affects maintenance, support, and change long after the initial schema is created. A modest investment in clear meaning and supported administration can prevent recurring ambiguity that would otherwise appear in every report and integration.
Review the model with difficult examples
Before implementation becomes expensive to change, walk through a small set of awkward cases. Change a customer's address after an order, merge duplicate accounts, cancel an approval, retry an import, and remove an employee from an organisation. Ask what must remain visible and which relationships should change.
Use those examples to review the schema, constraints, and application behaviour together. A diagram alone may look coherent while failing to represent a consequential transition. Record the decisions in language that future developers and business owners can understand.
Begin with the application's most important transaction and the facts it must preserve. A sound database design makes those facts durable, interpretable, and safe to change. Expand the model from that foundation, using real access patterns and operational evidence to guide further complexity.