在 Hexagonal 架构中,一个服务可以依赖另一个服务,还是紧耦合?
In Hexagonal architecture, can a service rely on another service, or is that tight coupling?
我正在创建银行应用程序。目前,只有一项主要服务,即交易服务。此服务允许通过 ID 获取交易并创建交易。为了创建交易,我首先要检查交易是否有效,方法是检查它试图从中扣除的账户余额,看看他们是否有足够的余额。现在我在做
TransactionController
呼叫 TransactionService
。 TransactionService
创建 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是操作的“视角”
我正在创建银行应用程序。目前,只有一项主要服务,即交易服务。此服务允许通过 ID 获取交易并创建交易。为了创建交易,我首先要检查交易是否有效,方法是检查它试图从中扣除的账户余额,看看他们是否有足够的余额。现在我在做
TransactionController
呼叫 TransactionService
。 TransactionService
创建 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是操作的“视角”