在 Hexagonal 架构中,一个服务可以依赖另一个服务,还是紧耦合?

In Hexagonal architecture, can a service rely on another service, or is that tight coupling?

我正在创建银行应用程序。目前,只有一项主要服务,即交易服务。此服务允许通过 ID 获取交易并创建交易。为了创建交易,我首先要检查交易是否有效,方法是检查它试图从中扣除的账户余额,看看他们是否有足够的余额。现在我在做

TransactionController 呼叫 TransactionServiceTransactionService 创建 Transaction,然后检查这是否是有效交易。此时,我创建了一个 AccountsService,它查询 AccountsRepository、returns 和 Account。然后我根据 Account.balance > Transaction.amount.

进行比较

我在这里意识到 TransactionService create 方法依赖于 AccountService get 方法。此外,AccountService 永远不会直接从控制器调用。

这是架构师的好方法,还是有更优雅的方法?

对于您的情况,我会说没关系,因为我猜如果 Account.balance < Transaction.amount 您真的不想继续进行交易。所以你必须在某个时候从 AccountService 获取所需的数据,没有办法解决这个问题。

如果你只是想触发一些副作用任务(比如发送电子邮件或其他东西),你可以依赖基于事件的方法,TransactionService 会发布一个事件和一个假设的 NotificationsService 会在某个时间点对它做出反应并做它的事情。

你的逻辑似乎没问题。如果您只需要来自交易服务(或其他服务)的账户服务,这是有效的。如果逻辑没有意义,则无需从控制器调用帐户服务。事实上,从控制器调用的某些服务可能会调用许多其他服务 - 例如电子邮件服务、文本消息服务等。

我不会创建帐户服务。我会从交易服务调用帐户回购。除此之外,我不会在知道交易对象是否有效之前创建交易对象。我会在创建交易之前检查条件。

您可以直接在 TransactionService 中引用您的 AccountRepository

抱歉我不会说 Java 但这是一个 C# 示例 :

public class Transaction {
  // implementation redacted

  public Transaction(decimal amount, Account from, Account to) {
    if(amount > from?.Balance) throw ... ;
    // redacted
  }
}
public class TransactionService {
  private readonly AccountRepository accounts; // by injection
  private readonly TransactionRepository transactions; // by injection

  public void AddTransaction(decimal amount, int source, int destination) {
    var from = accounts.Find(source); // throws if not found
    var to = accounts.Find(destination); // throws if not found
    var transaction = new Transaction(amount, from, to);
    transactions.Insert(transaction);
    transactions.Persist();
  }
}

但是,由于 Transaction 构造函数,此解决方案对 ORM 不太友好。另一种方法是使用 Account 作为根聚合,并将业务规则验证和实体关系处理代码放在那里:

public class Account {
  // implementation redacted

  public void AddTransaction(decimal amount, Account to) {
    if(amount > this.Balance) throw ... ;
    // more redacted validations
    this.Debitus.Add(new Transaction { Amount = amount, From = this, To = to });
  }
}
public class TransactionService {
  private readonly AccountRepository accounts; // by injection

  public void AddTransaction(decimal amount, int source, int destination) {
    var from = accounts.Find(source); // throws if not found
    var to = accounts.Find(destination); // throws if not found
    from.AddTransaction(amount, to);
    accounts.Persist();
  }
}

同意 @choquero70 我不喜欢编写或耦合 Serivce 的操作,因为“getBalance”可能出于一个或另一个目的而不同,我会在调用 AccountRepository 或您需要的任何回购或客户端的 TransactionService 上实现。 TransactionService是操作的“视角”