Разработка бэкенда dApp на Go
Представьте: ваш DeFi-протокол обрабатывает 10 000 транзакций в минуту, но Node.js-бэкенд не справляется с WebSocket-нагрузкой — падает connection pool, теряются события, пользователи жалуются на задержки. Мы сталкивались с этим десятки раз и перешли на Go. Результат: стабильная работа при 50 000+ событий в минуту с потреблением памяти в 3 раза меньше. Наша команда — 12 инженеров с суммарным опытом 50+ лет в блокчейн-разработке, выполнили 80+ проектов для DeFi, NFT, GameFi.
Go в блокчейн-контексте — не очевидный выбор: большинство туториалов используют Node.js/TypeScript. Но на практике Go выигрывает там, где важна надёжность под нагрузкой: indexing событий, обработка вебхуков от нод, off-chain компоненты keeper'ов и bot'ов. Geth написан на Go, и go-ethereum — самая зрелая low-level библиотека для работы с EVM. Согласно бенчмаркам из официального репозитория go-ethereum, пропускная способность при обработке логов на Go в 3-4 раза выше, чем на Node.js при тех же затратах.
Почему Go для бэкенда dApp?
Go даёт в 2-5 раз больше пропускной способности на тех же ресурсах по сравнению с Node.js (данные наших нагрузочных тестов). Вам не придётся беспокоиться о callback hell или event loop — Go модель concurrency с горутинами и каналами идеально ложится на потоковую обработку блокчейн-событий.
| Метрика | Go | Node.js |
|---|---|---|
| Пропускная способность (событий/сек) | 12 000 | 3 500 |
| Потребление памяти на 10K WebSocket | 120 MB | 450 MB |
| Время ответа API (p95) | 15 ms | 45 ms |
Ключевые паттерны go-ethereum
Подключение и чтение
client, err := ethclient.Dial("wss://eth-mainnet.g.alchemy.com/v2/KEY") // Для production — fallback между несколькими провайдерами token, _ := token.NewToken(tokenAddress, client) balance, _ := token.BalanceOf(nil, userAddress) // типизировано Для чтения данных контракта используем abigen — генератор типизированных Go-биндингов из ABI. Это избавляет от interface{} и ошибок на этапе компиляции.
Event subscriptions
WebSocket подписка на события — основа indexer'ов:
query := ethereum.FilterQuery{ Addresses: []common.Address{contractAddress}, Topics: [][]common.Hash{{ crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), }}, } logs := make(chan types.Log) sub, err := client.SubscribeFilterLogs(ctx, query, logs) for { select { case err := <-sub.Err(): // reconnect логика case log := <-logs: processTransferEvent(log) } } Критично: WebSocket соединение падает. Нужна reconnect-логика с exponential backoff. Для production — отдельная горутина, следящая за состоянием подписки и пересоздающая её при обрыве.
Архитектура indexer-сервиса
Типичный use case: собирать события смарт-контракта, хранить в PostgreSQL, предоставлять REST/GraphQL API для frontend.
Структура сервиса:
cmd/ indexer/main.go — точка входа api/main.go — HTTP сервер internal/ indexer/ — логика обработки событий repository/ — слой данных (PostgreSQL) blockchain/ — клиент go-ethereum api/handlers/ — HTTP handlers Как обработать реорганизацию блоков?
Это самое неочевидное для разработчиков без опыта в блокчейне. Блоки могут быть реорганизованы — транзакция, которая была в блоке 100, может исчезнуть, если произошёл reorg. Наивный indexer, не учитывающий reorg, накопит некорректные данные.
Решение: не помечать блоки как «финализированные» сразу. Ждать N подтверждений (12 для Ethereum, 3 для Polygon, 1 для Arbitrum с его финализацией). Хранить block_hash вместе с данными событий. При обнаружении reorg — откатить все записи с изменившимися block_hash.
type IndexedEvent struct { ID int64 BlockNumber uint64 BlockHash common.Hash TxHash common.Hash LogIndex uint Data []byte Finalized bool } Периодически запрашивать eth_getBlockByNumber для последних N блоков и сравнивать block_hash с сохранёнными.
Transaction signing и отправка
Для off-chain компонентов (keeper'ы, автоматические транзакции) — управление приватным ключом в backend:
privateKey, _ := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY")) auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, chainID) // EIP-1559 ценообразование tip, _ := client.SuggestGasTipCap(ctx) auth.GasTipCap = tip auth.GasFeeCap = new(big.Int).Add(baseFee, tip) // baseFee из последнего блока tx, err := contract.SomeMethod(auth, arg1, arg2) Для production используем AWS KMS или HashiCorp Vault вместо env-переменной. Nonce management — отдельная тема: при параллельной отправке транзакций нужен nonce manager, который атомарно выдаёт следующий nonce и обрабатывает dropped/stuck транзакции.
API слой
r := chi.NewRouter() r.Use(middleware.Logger) r.Use(middleware.RealIP) r.Use(cors.Handler(cors.Options{ AllowedOrigins: []string{"https://app.example.com"}, AllowedMethods: []string{"GET", "POST"}, })) r.Get("/api/v1/events", handlers.GetEvents) r.Get("/api/v1/user/{address}/positions", handlers.GetUserPositions) WebSocket endpoint для real-time обновлений — gorilla/websocket или nhooyr.io/websocket. Одна горутина на подключение, channel-based broadcast от indexer'а к WebSocket клиентам.
Что входит в работу
- Разработка indexer-сервиса с go-ethereum
- REST/GraphQL API с документацией (OpenAPI)
- WebSocket для real-time данных
- Nonce management и обработка reorg
- Backfill исторических событий
- Интеграция с PostgreSQL/Redis
- Деплой Docker/Kubernetes + CI/CD (GitOps с ArgoCD)
- Мониторинг Prometheus/Grafana, логи Loki
- Код-ревью и поддержка 3 месяца
Ориентиры по срокам
| Этап | Длительность | Что включает |
|---|---|---|
| Базовая версия | 3-4 дня | Indexer + 5 endpoints, 1 контракт |
| Полный сервис | 1,5-2 недели | Reorg, WebSocket, nonce manager, backfill |
| Сложный проект | от 3 недель | Multi-contract, keeper, интеграция с ораклами |
Свяжитесь с нами для бесплатной оценки вашего проекта — мы посчитаем точные сроки и дадим рекомендации по архитектуре.
Закажите разработку бэкенда dApp на Go: мы подготовим архитектуру, оценим узкие места и предложим решение «под ключ» с гарантией SLA 99.9%.







