DApp Architecture: Front End, Contracts and Data Layer
How the three layers of a decentralized app fit together, from the front end to smart contract logic and on-chain versus off-chain storage.

What DApp Architecture Changes About Building an App
A conventional web application runs on servers the company controls. The company can change the rules, suspend an account, or shut the service down, and users have no recourse in the software itself. DApp architecture is an attempt to remove that position of control from the design.
From One Server to a Network
Instead of a company-owned server, a dApp puts its core logic on a peer-to-peer network where every participating node holds a synchronized copy of the state. If some nodes go offline, the rest continue to process transactions, so there is no single machine whose failure stops the application.
The rules live in smart contracts published on the blockchain, where anyone can read the code and verify what it does. On Uniswap, for example, the swap contracts execute for anyone who calls them and no operator can reverse or block a settled trade. That is a claim about the contracts, not the whole product: Uniswap Labs runs the main web front end as an ordinary company, and a front end can be changed, geofenced, or taken down. Decentralization in a dApp is layer by layer, not all-or-nothing.
The Four Criteria in the Original Definition
The four-part definition of a dApp comes from The General Theory of Decentralized Applications, Dapps, a whitepaper by David Johnston and seven co-authors that has been published on GitHub since November 2013, before Ethereum launched. It sets out four requirements:
- Open source and user-governed: the application is fully open source, runs autonomously, no entity controls a majority of its tokens, and protocol changes are decided by consensus of its users.
- Records on a public blockchain: data and operating records are stored cryptographically on a public, decentralized blockchain, so there is no central point of failure.
- A native token: the application uses a cryptographic token that is required to access it, and those contributing value to the network are rewarded in that token.
- Token issuance by algorithm: tokens are generated by a standard cryptographic algorithm that acts as proof of the value nodes contribute.
Most applications that call themselves dApps today meet the first two criteria and treat the token requirements as optional. The definition is useful less as a test than as a list of the places where control can quietly be reintroduced.
The Three Layers
Almost every decentralized application, from an NFT minting page to a large DeFi protocol, separates into three layers: a front end users interact with, smart contracts holding the logic, and a storage layer for anything too large to put on-chain. Keeping them separate is what makes a dApp auditable and affordable to run.
DApp Architecture Layers Comparison
| Layer | Primary Function | Key Technologies | Responsibilities | Exposure |
|---|---|---|---|---|
| Front-End (UI) | Provide an interface for interacting with the dApp. | React, Vue.js, HTML, CSS, Ethers.js, viem | Rendering blockchain data, building and sending transactions, wallet integration (e.g., MetaMask). | Holds no funds directly, but a compromised front end can present a malicious transaction for the user to sign. |
| Smart Contracts | Execute the core business logic on the blockchain. | Solidity, Vyper, Rust (for Solana, etc.) | Defining rules, managing state changes, handling funds, ensuring transparent execution. | Highest. Code is normally immutable once deployed and controls assets directly. Audits are critical. |
| Data Storage | Store large files and off-chain data. | IPFS, Arweave, Filecoin, Swarm | Storing media, documents, and application data not suited to on-chain storage. | Content is addressed by hash, so tampering is detectable — but on IPFS, availability depends on someone continuing to pin the file. |
One note on that table: Web3.js, which appears in most older stack guides, is no longer a live option. Its GitHub repository is archived and its last npm release was version 4.16.0 in December 2024. New front ends use Ethers.js or viem.
The Front End
This is the part users see, built with ordinary web technology — React, HTML, JavaScript. What separates it from a normal website is a library such as Ethers.js or viem that connects the browser and the user's wallet to the blockchain.
The front end is responsible for:
- Reading data from the chain and displaying it in a readable form.
- Constructing transactions from what the user asked to do.
- Prompting the user to approve and sign those transactions in their own wallet.
Note what it does not do: it never holds the user's keys. The wallet signs, and the user approves each signature.

The Smart Contracts
The smart contracts are the dApp's backend. They run on the blockchain and hold the business logic, and once deployed their code normally cannot be altered, which is what makes the rules verifiable in advance.
A decentralized exchange's contract, for instance, fixes the exact formula used to price a swap. Because the logic runs on the chain rather than on a server, it keeps working as long as the network does, and no operator can selectively refuse to execute it.
The Data Layer
Blockchains are poor databases. Storing an NFT's artwork or a detailed user profile directly on-chain is slow and expensive, which is why most dApps put large files somewhere else.
The usual answer is IPFS. The file lives on the storage network, and the contract stores only a short hash pointing to it. That keeps the expensive on-chain footprint small while leaving the link verifiable, since altering the file would change its hash.
Smart Contracts: The Logic Layer
Smart contracts are deployed onto a chain such as Ethereum and, once live, are immutable unless they were deliberately built to be upgradeable. That cuts both ways: the rules cannot be changed against users, and bugs cannot be patched in place either.
Gas and Cost
Every operation a contract performs costs gas, paid by whoever sends the transaction. Inefficient code makes a dApp expensive to use, and storage is the most expensive thing a contract does.
Practical optimizations:
- Minimize state writes. Writing to storage costs far more than computation or reading from memory. Batch updates and avoid writing values you can derive.
- Pack storage, don't just shrink types. The EVM works in 32-byte words, so a lone
uint128saves nothing and can cost slightly more than auint256because of masking. The saving comes from packing several small values into a single 32-byte slot, which the compiler will do when you declare them adjacently. - Keep loops bounded. Iterating over an array that users can grow is how a function eventually becomes impossible to call within the block gas limit.
Current network costs can be checked on a gas tracker such as Etherscan's before choosing when to deploy or when to batch.
Security and Upgradability
Contracts routinely hold large balances, and a single vulnerability can drain them. That is why audit and security work accounts for a visible share of DeFi jobs.
Established practices:
- Use audited libraries. Standard implementations from OpenZeppelin have been reviewed far more widely than anything written from scratch.
- Follow checks-effects-interactions. Perform validation and update internal state before calling out to an external contract. This is the pattern that closes re-entrancy attacks, where a called contract calls back into yours before the first call has finished updating state.
- Decide on upgradeability deliberately. Proxy patterns separate the logic contract from the storage contract, so logic can be replaced without migrating user data. The trade-off is explicit: whoever controls the proxy can change the rules, which is a centralization users should be told about rather than discover.
Storing Data in a Decentralized System
Putting data directly on a blockchain is expensive enough that only the essentials belong there — ownership records, balances, the current state of a contract. Everything bulkier needs somewhere else to live that is not a company-owned server.
Off-Chain Storage
The most widely used option is the InterPlanetary File System (IPFS).
IPFS uses content addressing rather than location addressing. It derives a cryptographic hash from the file's contents, and that hash is the address. The contract stores the hash; when the dApp needs the file, it requests it from the network by hash. Because any change to the file produces a different hash, the link from the chain is self-verifying — you can always tell whether you received the file the contract pointed at.
The limitation worth stating plainly: content addressing guarantees integrity, not availability. An IPFS file stays retrievable only while at least one node pins it. Projects that want durability either run their own pinning infrastructure, pay a pinning service, or use a network like Arweave where storage is paid for up front.
The Hybrid Model
This split is the standard design for NFT marketplaces and other data-heavy dApps. An NFT's contract holds the ownership record and the hash; the image or video sits on IPFS.

- On-chain: ownership, balances, and the rules for changing them.
- Off-chain: media files and anything large enough that on-chain storage would price users out.
Interfaces That Hide the Blockchain
Users only ever see the front end. The work there is mostly in making wallet connection, transaction approval and network fees comprehensible to someone who has not read about any of them.
Making Transactions Legible
A generic "sign transaction" prompt tells the user nothing about what they are authorizing. Describing the action instead — "Confirm your bid of 0.5 ETH for this artwork" — is the difference between informed consent and a habit of clicking approve. This matters most for token approvals, where an unlimited allowance signed without explanation is a standing permission to move the user's tokens.
Confirmation time is the other friction point. A transaction that sits pending with no feedback looks like a failure. Interfaces that handle it well:
- Show the transaction moving through pending, processing and confirmed, with the transaction hash available.
- Say what is happening in one sentence, rather than showing an unexplained spinner.
- Let the user keep using the rest of the application while the transaction settles.
Wallet Connection and Errors
Connecting a wallet is the first step a new user has to complete, and an interface that detects installed wallets and suggests the likely one removes a decision the user is not equipped to make.
Errors are the other place raw blockchain output reaches users. Translating them is straightforward and rarely done:
| Technical Error | User-Friendly Message |
|---|---|
out of gas |
"The transaction ran out of gas before it finished. Try again with a higher gas limit." |
insufficient funds for gas |
"There isn't enough ETH in your wallet to cover the network fee for this transaction." |
user rejected |
"You cancelled the request in your wallet. If that was a mistake, try again." |
RPC error |
"We're having trouble reaching the network. Check your connection and refresh the page." |
Choosing a Development Stack
The stack determines how fast a team can test and deploy contracts and how easily it can reason about what it deployed. It should follow the team's existing skills rather than a ranking.
Core Frameworks
Two frameworks lead in the Ethereum ecosystem:
- Hardhat: a JavaScript and TypeScript environment with a large plugin ecosystem and a local network for testing and debugging. Hardhat suits teams who already work in JavaScript.
- Foundry: a Rust-based toolkit whose tests are written in Solidity rather than JavaScript. Foundry is noticeably faster on large test suites and includes fuzzing by default.
DApp Development Framework Comparison
| Framework | Tests Written In | Built On | Best For |
|---|---|---|---|
| Hardhat | JavaScript / TypeScript | Node.js | Teams already proficient in JavaScript, and projects that lean on the plugin ecosystem or need tight front-end integration. |
| Foundry | Solidity | Rust | Contract-heavy projects that want fast test runs, built-in fuzzing, and no context switch out of Solidity. |
Older guides also recommend Truffle, and it is worth knowing why they are out of date. Consensys announced the sunset of Truffle and Ganache on 21 September 2023, directed users to Hardhat and Foundry, and said both codebases would remain as public archives from 20 December 2023. Truffle's last npm release, version 5.11.5, was published on 13 September 2023. Existing Truffle projects still run; a new project started on it will be maintaining its own tooling.
The Rest of the Stack
A framework is not a stack. You also need a front-end library to talk to the chain — Ethers.js or viem — and node access. Running your own node is possible and gives you the strongest trust assumptions; most teams instead use a provider such as Alchemy or Infura and accept that they are trusting that provider's view of the chain. Worth knowing which trade-off you have made, because a provider outage takes a dApp's front end down as effectively as a server outage would.
Knowing how these pieces fit together is a standard requirement in blockchain jobs.
What Production DApps Do Differently
Uniswap is the clearest example of architectural restraint. Its swap logic sits in small, heavily optimized contracts rather than a sprawl of interacting ones, which keeps gas costs down and leaves less surface to attack. Its approach to change is equally deliberate: pools are deployed by a factory contract, and new versions of the protocol ship as entirely new deployments rather than upgrades to the existing ones. Liquidity in an older version is untouched by a newer version's release.
Splitting On-Chain and Off-Chain Work
OpenSea handles the opposite problem — metadata for millions of NFTs, which could never be economically stored on-chain. The division is clean:
- On-chain: ownership and transfer, handled by the contracts. Nothing more than needs to be there.
- Off-chain: the indexing, search, images, descriptions and collection data that make the marketplace usable, served from conventional databases and APIs.
The principle generalizes. Use the chain for what only the chain can do — verifiable ownership and state that nobody can unilaterally rewrite — and use ordinary infrastructure for everything else.
Usage Is Heavily Concentrated
Activity in the dApp ecosystem is not spread evenly. In a study of dApp utilization published in Frontiers in Blockchain on 16 August 2023, Mathias Bärtl of Hochschule Offenburg found that within his sample of 197 dApps tracked to 31 March 2023, the top 10% by transaction count accounted for 96% of all transactions, and the top 10% by trading volume accounted for close to 100% of volume. The bottom 30% contributed less than 1% of transactions between them.
That concentration is a reason to be clear-eyed about what a new dApp is competing for. For hiring and pay figures in this market, this site publishes its own: Web3 salaries by role, the Web3 jobs report by month and category, and the Web3 statistics page.
A Practical Build Sequence
Validate the Architecture First
Before a full build, prototype the parts that are expensive to change. Does the storage approach hold up at the volume you expect? Are the contract calls as cheap as you assumed when measured rather than estimated? A contract that is too expensive to call is a redesign, not a bug fix, and finding that out after an audit is the costly version.
The same Frontiers study is a caution against assuming growth. Bärtl also split each dApp's history into successive time segments and measured the trend in each. The share of segments with a statistically significant positive trend ran between roughly 60% and 80% for unique active wallets and transactions from the start of 2018 to early 2021, then declined; in the final segment, ending March 2023, it was below 10% for both, and around 17% for volume. You can read the full analysis in Bärtl's paper.
A Phased Checklist
- Phase 1: Contracts and testing
- Write the core logic using established patterns, including checks-effects-interactions.
- Build a test suite covering every function and the edge cases, including failure paths.
- Measure gas costs on the functions users will call most, and optimize from measurements rather than guesses.
- Phase 2: Off-chain and front-end integration
- Build the interface, with attention to how transactions and errors are explained.
- Integrate a front-end library such as Ethers.js or viem.
- Connect decentralized storage such as IPFS, and decide who is pinning the content.
- Phase 3: Audit and deployment
- Commission a third-party security audit of the contracts. For anything holding user funds this is not optional.
- Fix every critical and high-severity finding, and publish the report.
- Deploy to a public testnet and run the full flow there.
- Launch on mainnet and monitor the first transactions closely.
- Phase 4: Post-launch
- Monitor contract events and network performance.
- Have an upgrade path decided in advance, and be explicit with users about who controls it.
- Collect user feedback and feed it into the next cycle.
Web3 teams hire for exactly these skills: contract development, auditing, and front-end work that makes any of it usable. You can browse current openings on the Find Web3 job board, or see which companies are hiring.