Project Icon

trading

Scala 3驱动的事件架构参考实现

项目采用Scala 3实现事件驱动架构,包括交易处理、预测、快照和警报等微服务。使用Apache Pulsar进行消息通信,提供Web界面实现实时交易警报订阅。集成了监控、分布式追踪和自动化测试,可作为构建现代事件驱动系统的参考。

trading

CI Elm CI Scala CI Tyrian CI Registry CI Smokey

Reference application developed in the Functional event-driven architecture: Powered by Scala 3 book.

Table of contents

Web App

The web application allows users to subscribe/unsubscribe to/from symbol alerts such as EURUSD, which are emitted in real-time via Web Sockets.

client

It is written in Elm and can be built as follows.

$ nix build .#elm-webapp
$ open result/index.html # or specify browser

There's also a development shell handy for local development.

$ nix develop .#elm
$ cd web-app
$ elm make src/Main.elm --output=Main.js
$ open index.html # or specify browser

If Nix is not your jam, you can install Elm by following the official instructions and then compile as usual.

$ cd web-app
$ elm make src/Main.elm --output=Main.js
$ xdg-open index.html # or specify browser

ScalaJS

There is also a replica of the Elm application written in Scala using the Tyrian framework that can be built as follows.

$ sbt 'webapp/fullLinkJS'

You can then run it via Nix as shown below (it requires flakes).

$ nix run .#tyrian-webapp
Using cache dir: /home/gvolpe/workspace/trading/modules/ws-client/nix-parcel-cache
Server running at http://localhost:1234
✨ Built in 7ms

NOTICE: The nix run command will create a directory for the Parcel cache, which needs write permissions.

We use fullLinkJS to create a fully optimized JS file. However, we can use fastLinkJS for faster iterations.

For such cases, it may be more convenient to use yarn directly.

$ nix develop .#tyrian
$ cd modules/ws-client
$ yarn install
$ yarn build
$ yarn start
yarn run v1.22.17
parcel index.html --no-cache --dist-dir dist --log-level info
Server running at http://localhost:1234
✨ Built in 1.82s

However, this is not fully reproducible and can't be guaranteed this will work in the future.

Without Nix, you need to install yarn and parcel, and use yarn as shown above.

Overview

Here's an overview of all the components of the system.

overview

  • Dotted lines: Pulsar messages such as commands and events.
  • Bold lines: read and writes from / to external components (Redis, Postgres, etc).

Requirements

The back-end application is structured as a mono-repo, and it requires both Apache Pulsar and Redis up and running. To make things easier, you can use the provided docker-compose.yml file.

Build JDK image

The docker-compose file depends on declared services to be published on the local docker server. All docker builds are handled within the build.sbt using sbt-native-packager.

To build all of the service images, we have a few options.

The first one via the given Dockerfile.

$ docker build -t jdk17-curl modules/

The second one via Nix, from where we can build a slim image also based on openjdk:17-slim-buster.

$ nix build .#slimDocker -o result-jre
$ docker load -i result-jre

The third one also via Nix, though building a layered image based on the same JDK we use for development.

$ nix build .#docker -o result-jre
$ docker load -i result-jre

The main difference between these three options is the resulting image size.

$ docker images | rg jdk17
jdk17-curl                    latest               0ed94a723ce3   10 months ago   422MB
jdk17-curl-nix                latest               c28f54e42c21   52 years ago    557MB
jdk17-curl-slim               latest               dbe24e7a7163   52 years ago    465MB

Any image is valid. Feel free to pick your preferred method.

NOTE: As of September 2022, the Docker image resulting from nix build .#docker is no longer compatible with sbt-native-packager, so either go for nix build (defaults to the slim image) or build it directly via Docker with the given Dockerfile.

Build service images

Once the base jdk17-curl image has been built, we can proceed with building all our services' images.

$ sbt docker:publishLocal

Run dependencies: Redis, Kafka, etc

$ docker-compose up -d pulsar redis

pulsar

To run the Kafka Demo (see more below in X Demo), kafka.yml should be used instead.

$ docker-compose -f kafka.yml up

Running application

If we don't specify any arguments, then all the containers will be started, including all our services (except feed), Prometheus, Grafana, and Pulsar Manager.

$ docker-compose up
Creating network "trading_app" with the default driver
Creating trading_pulsar_1 ... done
Creating trading_redis_1  ... done
Creating trading_ws-server_1      ... done
Creating trading_pulsar-manager_1 ... done
Creating trading_alerts_1         ... done
Creating trading_processor_1      ... done
Creating trading_snapshots_1      ... done
Creating trading_forecasts_1      ... done
Creating trading_tracing_1        ... done
Creating trading_prometheus_1     ... done
Creating trading_grafana_1        ... done

It is recommended to run the feed service directly from sbt whenever necessary, which publishes random data to the topics where other services are consuming messages from.

Services

The back-end application consists of 9 modules, from which 5 are deployable applications, and 3 are just shared modules. There's also a demo module and a web application.

modules
├── alerts
├── core
├── domain
├── feed
├── forecasts
├── it
├── lib
├── processor
├── snapshots
├── tracing
├── ws-client
├── ws-server
├── x-demo
└── x-qa

backend

Lib

Capability traits such as Logger, Time, GenUUID, and potential library abstractions such as Consumer and Producer, which abstract over different implementations such as Kafka and Pulsar.

Domain

Commands, events, state, and all business-related data modeling.

Core

Core functionality that needs to be shared across different modules such as snapshots, AppTopic, and TradeEngine.

Feed

Generates random TradeCommands and ForecastCommands followed by publishing them to the corresponding topics. In the absence of real input data, this random feed puts the entire system to work.

Forecasts

Registers new authors and forecasts, while calculating the author's reputation.

Processor

The brain of the trading application. It consumes TradeCommands, processes them to generate a TradeState and emitting TradeEvents via the trading-events topic.

Snapshots

It consumes TradeEvents and recreates the TradeState that is persisted as a snapshot, running as a single instance in fail-over mode.

Alerts

The alerts engine consumes TradeEvents and emits Alert messages such as Buy, StrongBuy or Sell via the trading-alerts topic, according to the configured parameters.

WS Server

It consumes Alert messages and sends them over Web Sockets whenever there's an active subscription for the alert.

Tracing

A decentralized application that hooks up on multiple topics and creates traces via the Open Tracing protocol, using the Natchez library and Honeycomb.

tracing

Tests

All unit tests can be executed via sbt test. There's also a small suite of integration tests that can be executed via sbt it/test (it requires Redis to be up).

X Demo

It contains all the standalone examples shown in the book. It also showcases both KafkaDemo and MemDemo programs that use the same Consumer and Producer abstractions defined in the lib module.

Pulsar CDC

To run the Pulsar CDC Demo, you need Postgres and Pulsar (make sure no other instances are running). Before running them, we need to download the connector NAR file.

$ mkdir -p pulsarconf/connectors && cd pulsarconf/connectors
$ wget https://archive.apache.org/dist/pulsar/pulsar-2.10.1/connectors/pulsar-io-debezium-postgres-2.10.1.nar
$ docker-compose -f pulsar-cdc.yml up

Once both instances are up and healthy, we can run the Pulsar Debezium connector.

$ docker-compose exec -T pulsar bin/pulsar-admin source localrun --source-config-file /pulsar/conf/debezium-pg.yaml

You should see this in the logs.

Snapshot step 3 - Locking captured tables [public.authors]

X QA

It contains the smokey project that models the smoke test for trading.

Monitoring

JVM stats are provided for every service via Prometheus and Grafana.

grafana

Topic compaction

Two Pulsar topics can be compacted to speed-up reads on startup, corresponding to Alert and TradeEvent.Switch.

To compact a topic on demand (useful for manual testing), run these commands.

$ docker-compose exec pulsar bin/pulsar-admin topics compact persistent://public/default/trading-alerts
Topic compaction requested for persistent://public/default/trading-alerts.
$ docker-compose exec pulsar bin/pulsar-admin topics compact persistent://public/default/trading-switch-events
Topic compaction requested for persistent://public/default/trading-switch-events

In production, one would configure topic compaction to be triggered automatically at the namespace level when certain threshold is reached. For example, to trigger compaction when the backlog reaches 10MB:

$ docker-compose exec pulsar bin/pulsar-admin namespaces set-compaction-threshold --threshold 10M public/default
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号