本文主要简单分析一下DApp的前后端架构
如果想成为一名DApp开发者,那么最好对DApp的整个前服务端架构都有一个基本的理解。与传统的App(包括Web App、Mobile App和Client App)最大的不同点在于, DApp的核心功能需要与智能合约直接交互的。所以理解DApp中前端、服务端、数据存储和智能合约之间的通信关系,就能很好的理解DApp的整体架构。
现在市面上大部分Dapp都是Web App,而且不提供Mobile App版本,一部分原因是因为构建一个跨平台钱包方案较为复杂,并且大部分用户都使用的例如MetaMask这样的浏览器插件,在Web平台上目前开发DApp的开发效率更高而且体验更好。所以本文的架构分析也将主要以Web App去讲解。
在谈论DApp架构前,我们先来看传统Web2.0的App的架构一般是怎么样的。
不考虑较为复杂的微服务架构和其他RPC等调用, Web2.0时代传统的单体App架构一般如上图所示,由前端、服务端和数据库三部分组成:
在前文中,我们有提到DApp和传统的App最大的不同点在于部署到区块链上的智能合约。所以在讲解DApp架构前,我们先来理清DApp的几个与智能合约相关的概念。
通常指运行在EVM兼容网络中的Solidity或其他合约语言代码,它们负责与用户交易我们发行的数字资产、存储DApp的链上状态。 智能合约中还有两个比较重要的概念:
Provider/Signer是DApp开发的重要组成部分。智能合约可以在区块链上读取数据和写入数据(即执行改变区块链状态的交易),要从区块链中读取数据,你需要Provider(提供者);要写入数据,你需要Signer(签名者)。
如果你有使用过web3.js或者ethers.js这类库的经历,那么肯定对于Provider/Signer的概念不会陌生,因为你首先就得设置Provider:
const Web3 = require('web3');
const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
MetaMask是以太坊中最流行的钱包,同时也是目前开发DApp最流行的InjectProvider。
但MetaMask其实是依赖于Infrua这个服务提供商,这类服务提供商也被称为API 提供商 或 IaaS,它们提供了全节点和一些开发套件来帮助开发者更方便的访问区块链,你不用运行自己的区块链节点。
你可以在ethers.js中直接使用InfruaProvider
:
const { ethers } = require("ethers");
const provider = new ethers
.providers
.InfuraProvider('rinkeby',INFURA_PROJECT_ID);
如果你想了解Infrua和Metamask的关系?以及什么是服务提供商?你可以参考Metamask官网的这篇文章《What is Infura, and why does MetaMask use it?》。市面上流行的服务提供商除了Infura,还有Alchemy、Quicknode、Moralis 或者 Pocket 等,它们通过RPC协议与以太坊节点进行通信。
上图是一个DApp的基础架构图,你图中可以看到,我们可以在前端直接通过Provider的区块链节点访问区块链网络,也可以在服务端进行访问。因为我们构建的是DApp(去中心化应用),链下的数据(视频、图片、元数据和其他业务数据)最好以去中心化存储方案存储,下文我们会对DApp的数据存储进行介绍。
作为DApp的前端开发,web3.js或者ethers.js这两个基础类库是你应该熟悉。在项目的脚手架,你可以参考scaffold-eth这个项目。
至于前端框架的选择,你可以选择你熟悉的Web开发框架。例如Vue或React,但是这边我更加推荐React或Next.js,因为React的社区中有更多活跃的Web3开发者和依赖库,例如web3-react、wagmi,它们提供了方便的hook来让你与Provider和合约进行交互。
DApp的前端开发与web2的前端开发不同的主要在于以下几点:
fetch
或者axios
。在DApp中,你如果直接通过前端读取区块链网络的数据,需要基于Provider,你同样通过上面的类库完成。需要注意的的是,当你尝试从智能合约中读取数字时,你获取的值是BigNumber
而不是number
,你需要进行类型转换。如果你是一个前端开发者或者一个刚入行的web3从业者,你可能倾向于绕过服务端,仅依赖前端和智能合约交互。但是目前市面上大部分的DApp都有服务端参与,只有少数产品仅仅只有前端和智能合约两个部分,例如Uniswap。
如果你期望你的DApp有着更好的用户体验和响应速度,通常来说引入服务端是一个更好的方式。原因之一是链上存储的成本过高,反复的签名对用户来说体验是不好的。如果你构建的一个大型DApp,例如像Odysee,你绝大多数的数据(视频、图片、主页信息等)不需要在链上进行存储,而是通过传统的HTTP交互,并将核心的hash记录存储在链上即可。
这边服务端语言推荐使用Node.js,这样可以复用一部分的代码,而且在和区块链服务提供商交互的逻辑也基本一致,你还可以使用类似Firebase等FaaS服务去编写你的服务端业务。
DApp的服务端和传统的Web2.0服务端大致的区别在于:
智能合约是整个DApp核心中的核心,智能合约的编写推荐使用Hardhat或Truffle框架,他们提供了智能合约的开发、测试、部署的整套开发环境,同时还有丰富的插件系统。
在智能合约中除了业务逻辑的编写外,有两点是你需要注意的:
storage
、memoery
,每种位置产生的费用有很大的不同。合约的函数也需要有对应的函数类型声明,view
函数 与pure
函数在外部调用时不需要承担 gas 费用。你可以考虑在合适的时间引入MerkleProof,这是它的JavaScript实现。除了引入服务端的DApp经常被诟病说不是完全的去中心化外,存储方案也是经常被同时提及的。笔者看来,并不是什么功能都应该“分布式”和“去中心化”,对于数据的存储,还是应该更多样化,提供最符合数据存储需求的解决方案。
例如你想创建一个像Odysee这样的去中心化视频网站,在链上存储的状态应该是媒体文件和内容文本的hash值,原始的视频、图片、视频标题、描述等数据,不应该上链存储。
对于链下存储,如果你想更加的去中心化,你可以将其存储在去中心化存储网络中,流行的去中心化存储方案有:
欢迎关注我的同名vx公众号:熠辉Web3。了解更多区块链DApp开发教程
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!