Skip to content

Commit

Permalink
feat: add basic architecture dsl sample
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Jun 28, 2023
1 parent 46133c9 commit 77564ba
Showing 1 changed file with 121 additions and 27 deletions.
148 changes: 121 additions & 27 deletions src/dsl-driven-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

```yml
EventStorming:
Domain { Name: "电影订票系统" }
Event { Name: "用户提交订单" }
Triggered By: "用户选择电影、场次、座位,确认订单"
Description: "用户提交订单,包括所选电影、场次、座位等信息"
Actors: ["用户"]
Action: "将用户提交的订单信息保存到订单数据库中"
Outcome: "订单状态被标记为已提交"
Domain { Name: "电影订票系统" }
Event { Name: "用户提交订单" }
Triggered By: "用户选择电影、场次、座位,确认订单"
Description: "用户提交订单,包括所选电影、场次、座位等信息"
Actors: [ "用户" ]
Action: "将用户提交的订单信息保存到订单数据库中"
Outcome: "订单状态被标记为已提交"
```

## 常规 DSL
Expand Down Expand Up @@ -45,7 +45,8 @@ System("BlogSystem") {
它可以分析某一个场景的业务,基于这个业务做分析。在这个 DSL,反复让 ChatGPT 设计之后,勉强可以详细拆开任务:

- Operation。通过 Ops 的输入、输出、先验条件、后验条件,我们可以构建出更准确的函数。
- Entitiies。是可独立从 DSL 拆解出来的,并且与数据库结构是绑定的,所以可以用来做数据库设计(ChatGPT 设计了一个诡异的 []? 语法 )。
- Entitiies。是可独立从 DSL 拆解出来的,并且与数据库结构是绑定的,所以可以用来做数据库设计(ChatGPT 设计了一个诡异的 []?
语法 )。
- API。API 其实对于编码的帮助是有限的,不过其最大的用处是用于自动化测试,用于确保 ChatGPT 的代码是正确的。

所以,我们只需要拆解任务,并发送到各个管道里,再 merge 一下,就可能能得到一份可工作的代码。至于,前端部分,我们可以用类似的方式来设计。
Expand All @@ -62,7 +63,8 @@ System("BlogSystem") {

```markdown
HasMatchFunction: true
Thought: I need to introduce the system to the team and ensure that it aligns with our overall architecture and governance policies.
Thought: I need to introduce the system to the team and ensure that it aligns with our overall architecture and
governance policies.
Action: introduce_system
Action Input: https://github.com/archguard/ddd-monolithic-code-sample
```
Expand All @@ -77,7 +79,7 @@ const thoughtRegex = /\s*Thought:\s*(.*(?:\n(?!\s*\/\/).*)*)/i;
const actionRegex = /\s*Action:\s*(.*(?:\n(?!\s*\/\/).*)*)/i;
const actionInputRegex = /\s*Action\s*Input:\s*(.*(?:\n(?!\s*\/\/).*)*)/i;

function messageToThought(splitContent: string[]) {
function messageToThought (splitContent: string[]) {
let thought = thoughtRegex.exec(splitContent[0])?.[1] ?? "";
let action = ""
if (splitContent.length >= 2) {
Expand All @@ -99,6 +101,98 @@ function messageToThought(splitContent: string[]) {
}
```
## 内部 DSL
内部DSL是一种特殊的DSL,它的语法与宿主编程语言的语法相同或相似,并且可以直接嵌入到宿主编程语言中,不需要额外的解析器。
这使得开发人员能够以一种更直观、声明性的方式来描述特定领域的问题和解决方案。
特点如下:
- 与编程语言的语法相同:内部DSL的语法与宿主编程语言的语法相同或相似,因此可以直接嵌入到宿主编程语言的代码中,不需要额外的解析器或转换器。这使得内部DSL更容易理解、编写和维护,因为开发人员可以利用已经熟悉的编程语言知识和工具。
- 直接嵌入到宿主语言:内部DSL可以直接嵌入到宿主编程语言的代码中,并与宿主语言的功能和库进行无缝集成。这意味着可以在内部DSL中直接调用宿主语言的函数、类和其他特性,从而充分利用宿主语言的强大功能和生态系统。
- 基于宿主语言的类型系统和语义:由于内部DSL直接嵌入到宿主编程语言中,它可以完全利用宿主语言的类型系统和语义。这使得内部DSL可以提供更严格的类型检查和编译时验证,并且可以与宿主语言的工具链和开发环境无缝集成,例如代码补全、静态分析和调试。
- 可扩展性:内部DSL可以利用宿主编程语言的灵活性和可扩展性进行自定义和扩展。开发人员可以使用宿主语言的特性来定义新的DSL构造,增加DSL的表达能力和领域特定性。
以Kotlin语言为例,它提供了强大的内部DSL支持。我们可以利用Kotlin的语法和特性来创建具有领域特定性的DSL,并将其嵌入到Kotlin代码中。
详细可以参考:https://kotlinlang.org/docs/type-safe-builders.html
在 Co-mate 中,我们便主要采用这种方式来描述软件的架构:
```kotlin
architecture {
system("TicketBooking") {
connection("Reservation" to "Ticket")
}
}
```
对应的示例实现代码:
```kotlin
fun architecture(function: ArchitectureSpec.() -> Unit): ArchitectureSpec {
val spec = ArchitectureSpec()
spec.function()
return spec
}

class ArchitectureSpec : Spec<String> {
override fun default(): Spec<String> {
return defaultSpec()
}

override fun exec(element: String): List<RuleResult> {
return listOf()
}

fun system(systemName: String, function: SystemDeclaration.() -> Unit): SystemDeclaration {
val system = SystemDeclaration(systemName)
system.function()
return system
}

companion object {
fun defaultSpec(): ArchitectureSpec {
return architecture {
system("TicketBooking") {
connection("Reservation" to "Ticket")
}
}
}
}
}

class ConnectionDeclaration(val source: String, val target: String) : BaseDeclaration<String>
class SystemDeclaration(name: String) : BaseDeclaration<String> {
fun component(componentName: String, function: ComponentDeclaration.() -> Unit): ComponentDeclaration {
val component = ComponentDeclaration(componentName)
component.function()
return component
}

fun connection(pair: Pair<String, String>, function: ConnectionDeclaration.() -> Unit?): ConnectionDeclaration {
val connection = ConnectionDeclaration(pair.first, pair.second)
connection.function()
return connection
}

fun connection(pair: Pair<String, String>): ConnectionDeclaration {
return ConnectionDeclaration(pair.first, pair.second)
}

}

class ComponentDeclaration(name: String) : BaseDeclaration<String> {
fun module(moduleName: String, function: ModuleDeclaration.() -> Unit): ModuleDeclaration {
val module = ModuleDeclaration(moduleName)
module.function()
return module
}
}

class ModuleDeclaration(name: String) : BaseDeclaration<String>
```
## 端到端 DSL 示例
### 阶段一:让 ChatGPT 与你学 DDD
Expand All @@ -109,9 +203,9 @@ function messageToThought(splitContent: string[]) {
```yaml
ContextMap TicketBooking {
Reservation -> Cinema;
Reservation -> Movie;
Reservation -> User;
Reservation -> Cinema;
Reservation -> Movie;
Reservation -> User;
}
```
Expand All @@ -121,11 +215,11 @@ ContextMap TicketBooking {
```yaml
ContextMap:
Subdomain { Name: [subdomain_name] }
[CollaboratesWith: [other_subdomain_name], ...]
[ConformistWith: [other_subdomain_name], ...]
[Antagonizes: [other_subdomain_name], ...]
...
Subdomain { Name: [ subdomain_name ] }
[ CollaboratesWith: [ other_subdomain_name ], ... ]
[ ConformistWith: [ other_subdomain_name ], ... ]
[ Antagonizes: [ other_subdomain_name ], ... ]
...
```
第二个问题:用这个 ContextMap DSL 对在线电影订票系统建模。对于其它问题也是相似的。
Expand All @@ -138,13 +232,13 @@ ContextMap:
```yaml
EventStorming:
Domain { Name: "电影订票系统" }
Event { Name: "用户提交订单" }
Triggered By: "用户选择电影、场次、座位,确认订单"
Description: "用户提交订单,包括所选电影、场次、座位等信息"
Actors: ["用户"]
Action: "将用户提交的订单信息保存到订单数据库中"
Outcome: "订单状态被标记为已提交"
Domain { Name: "电影订票系统" }
Event { Name: "用户提交订单" }
Triggered By: "用户选择电影、场次、座位,确认订单"
Description: "用户提交订单,包括所选电影、场次、座位等信息"
Actors: [ "用户" ]
Action: "将用户提交的订单信息保存到订单数据库中"
Outcome: "订单状态被标记为已提交"
```
第五个问题……
Expand All @@ -161,8 +255,8 @@ EventStorming:
```markdown
generate java-code from-domain-model
target-package: com.example.movieticket.order
source-model: order-domain-model
target-package: com.example.movieticket.order
source-model: order-domain-model

service-mapper 订单服务映射
map-method: 查询电影
Expand Down

0 comments on commit 77564ba

Please sign in to comment.