Estoy creando una aplicación bancaria. Actualmente, hay un servicio principal, el servicio de transacciones. Este servicio permite obtener transacciones por id y crear transacciones. Para crear una transacción, primero quiero verificar si la transacción es válida, verificando el saldo de la cuenta de la que está tratando de deducir y ver si tienen saldo suficiente. Ahora mismo estoy haciendo
TransactionController llama a TransactionService . TransactionService crea Transaction y luego verifica si se trata de una transacción válida. En este punto, he creado un AccountsService , que consulta el AccountsRepository , devuelve una Account . Luego hago la comparación basada en Account.balance > Transaction.amount .
Soy consciente aquí de que el método de creación de TransactionService se basa en el método de obtención de AccountService . Además, AccountService nunca se llama directamente desde un controlador.
¿Es esta una buena forma de arquitecto, o hay una forma más elegante de hacerlo?
En su caso, diría que está bien porque supongo que si Account.balance < Transaction.amount realmente no desea continuar con la transacción. Por lo tanto, en algún momento debe obtener los datos necesarios de AccountService , no hay forma de evitarlo.
Si solo quisiera desencadenar alguna tarea de efecto secundario (como enviar un correo electrónico o algo así), podría confiar en un enfoque basado en eventos en el que TransactionService publicaría un evento y un hipotético NotificationsService reaccionaría ante él en algún momento y lo haría. su cosa
Tu lógica parece estar bien. Si solo necesita el Servicio de Cuenta del servicio de Transacción (u otro Servicio), esto es válido. No es necesario llamar a un servicio de cuenta desde un controlador solo para hacerlo si la lógica no tiene sentido. De hecho, algunos Servicios que se invocan desde un Controlador pueden llamar a muchos otros servicios, como un Servicio de correo electrónico, un Servicio de mensajes de texto, etc.
Puede hacer referencia a su AccountRepository directamente en su TransactionService .
Lo siento, no hablo Java, pero aquí hay un ejemplo de 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(); } } Sin embargo, esta solución es menos compatible con ORM debido al constructor Transaction . Otra forma de evitarlo sería usar Account como su agregado raíz y colocar allí el código de validación de reglas comerciales y manejo de relaciones entre entidades:
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(); } }