这样的工作流程应使用哪种设计模式?

时间:2018-08-20 02:54:48

标签: design-patterns architecture workflow microservices soa

假设我有这样的工作流程

          (1) Check for latest commit in a GitHub repo
                             |
                             |
                    (2) Is new commit?
                            / \
                        No /   \ Yes
                          /     \
                         /       \
                      End         \
                                   \
                                    \
                                  (3) Download all files
                                  /       |       \
                                 /        |        \
                     (4i) Process file i  .      Process file N
                                \         .         /
                                 \        .        /
                                  \       .       /
                                   \      .      /
                                    \     .     /
                                        End

我想拥有以下微服务:

  • (A)提交检查器
  • (B)文件下载器
  • (C)文件处理程序

我的问题是,这些微服务中的每一个是否应该是顺序工作流中的链接,还是这些微服务是否应该仅包含功能,而单独的“无所不知”微服务负责工作流?

在第一种情况下,可能看起来像

A: (1),(2) ====> B:(3) ====> C:(4i),(4ii),...,(4N)

每个链接将消息写入队列中的消息,然后由下一个链接拾取。

因为(2)在A中,这意味着A正在决定是否调用B。我猜您可以说这两个服务紧密耦合。

在我建议的其他实现中,将有一个单独的服务X执行控制流,并将数据简单地插入到A,B,C中,这将执行诸如以下的单个任务

A: Get latest commit
B: Download all files by commit
C: Process single file

然后X将拥有/执行的逻辑

  • (2),即决定是终止工作流程还是执行下一步
  • 决定并行处理下载的文件

哪个实施更好?

我还有一个关于存储的问题。 Is new commit?部分意味着我保留了一份提交记录,因此我可以知道我检查的内容是否是我以前从未检查过的内容。 谁应该保留提交记录?应该在A的存储中还是单独的“工作流存储”中考虑它?

2 个答案:

答案 0 :(得分:1)

我要解决的方法是成为Chain of Responsibilities,其中每个节点负责检查条件或完成工作

例如,您的第一阶段是一个合理的决定:

  1. “新提交?” {true | false} D
  2. 如果为true,“停止操作”会停止吗? (访客?)

因此,每个节点都有可能执行终止功能(可能会/可能不会返回结果;我的功能主义者说应该这样做)。

从编码角度看,它可能像这样:

interface Responsibility<I> {
    fun apply(context:I) : Unit?
}

class GitRepo{
    val files = setOf<URI>()
    val commiters = setOf<String>()
    fun hasNewCommit() = true

}

abstract class Chained<I,N>(protected val next:Responsibility<N>) : Responsibility<I> {


}
class CheckCommited(next:Responsibility<Set<URI>>) : Chained<GitRepo,Set<URI>>(next) {
    override fun apply(gr:GitRepo) = when(gr.hasNewCommit()) {
        true -> next.apply(gr.files)
        false -> null
    }
}
class DownLoadFiles(next:Responsibility<Set<URL>>) : Chained<Set<URI>,Set<URL>>(next) {
    override fun apply(uris:Set<URI>) {
        next.apply(uris.map { downLoad(it) }.toSet())
    }
    fun downLoad(uri:URI): URL {
        return uri.toURL()
    }
}

class NotifyPeople(val people: Set<String>) : Responsibility<Set<URL>> {

    override fun apply(context: Set<URL>): Unit? {
        people.forEach {  context.forEach { sendMail(it.toString()) }}
    }

    private fun sendMail(email: String) {

    }
}

val root = CheckCommited(DownLoadFiles(NotifyPeople(setOf("joe@company.com","suzy@someplace.other"))))

答案 1 :(得分:1)

那不是微服务。这是微微服务,恕我直言,它们是小型的,可以激发微服务所需的所有额外维护和管理。在GOTO Conference 2014的精彩演讲中,Martin Fowler建议为每个微服务推荐几个全职开发人员。如果您没有至少一个可以全天候使用该服务的设备,那么它的体积就会很小。

为简单起见,您可以改用基于消息的体系结构,其中每个内部服务都订阅来自其他内部服务的消息。

例如,下载完成后,您可以从FileDownloaded发送一个FileDownloader已订阅的FileProcessor事件。这样,它们之间也不会紧密耦合。

对于消息传递库,例如,您可以使用我的messaging library