#some help needed with fixing circular dependencies

34 messages · Page 1 of 1 (latest)

thin basin
#

hey guys. can smb help me out with fixing circular dependencies?

The dependencies of some of the beans in the application context form a cycle:

   infoRestController
┌─────┐
|  BGWService defined in file 
↑     ↓
|  importService defined in file 
└─────┘

This error doesnt even make sense, wtf?

magic sleetBOT
#

This post has been reserved for your question.

Hey @thin basin! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically closed after 300 minutes of inactivity.

TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.

formal remnant
#

Well, make either importservice not depend on bgwservice or the other way around

thin basin
formal remnant
#

There's a workaround, if you annotate a non-final field with @AutoWired instead of the constructor

#

But circular dependencies are a sign that there's something wrong with your classes

#

They shouldn't depend on each other

thin basin
#
@Service
@Slf4j
public class BGWService {

    ImportService importService;
    CustomerRepository customerRepository;
    AppConfig appConfig;
    XmlUtil xmlUtil;
    PaymentRepository paymentRepository;

    public BGWService(AppConfig appConfig, XmlUtil xmlUtil, CustomerRepository customerRepository, PaymentRepository paymentRepository, ImportService importService) {
        this.appConfig = appConfig;
        this.xmlUtil = xmlUtil;
        this.customerRepository = customerRepository;
        this.paymentRepository = paymentRepository;
        this.importService = importService;
    }
    public void createPaymentpaymentConfirmation(PaymentDTO paymentConfirmationRequest) throws NoEarlierPaymentException, JAXBException, IOException {
        log.info("BGWService.createPaymentpaymentConfirmation");

        Optional<Payment> earlierPayment=paymentRepository.findByTransactionIdAndAmount(paymentConfirmationRequest.getTransactionId(), paymentConfirmationRequest.getAmount());
        if (earlierPayment.isEmpty()) {
            log.info("BGWService.createPaymentConfirmation.NoEarlierPaymentException.TransactionId:{}.Amount:{}",paymentConfirmationRequest.getTransactionId(),paymentConfirmationRequest.getAmount());

            throw new NoEarlierPaymentException("No earlier payment with transactionId:"+paymentConfirmationRequest.getTransactionId()+" and amount:"+paymentConfirmationRequest.getAmount());
        }

        Payment payment=PaymentMapper.toEntity(paymentConfirmationRequest);
        importService.importStatement(appConfig.getSystemUserId());
        paymentRepository.save(payment);
    }
}
#
public class ImportService {

    LmsService lmsService;
    XmlUtil xmlUtil;
    BGWService bgwService;
    AppConfig appConfig;

    @Autowired
    DivisionRepository divisionRepository;

    @Autowired
    CashRegRepository cashRegRepository;

    @Autowired
    CustomerIDResolverService customerIDResolverService;

    @Autowired
    BankAccountConfig bankAccountConfig;

    DateTimeFormatter formatterCurrent = DateTimeFormatter.ofPattern("yyyyMMdd");
    DateTimeFormatter formatterEod = DateTimeFormatter.ofPattern("yyyyMMdd");

    //constructor


    private String generateFileName(String type, DateTimeFormatter formatter, LocalDateTime date) {
        return type + date.format(formatter) + ".xml";
    }

    public void importSbankStatement(Long userId) throws JAXBException, IOException {
        log.info("importSbankStatement started");
        for (BankAccount bankAccount : bankAccountConfig.getSbankBankAccounts()) {
            log.info("iban: " + bankAccount.getIban());
            ResponseEntity<String> accountStatementResponse = bgwService.getAccountStatementResponse();
            if (accountStatementResponse.getStatusCode().value() == 200) {
                HttpHeaders accountStatementResponseHeaders = accountStatementResponse.getHeaders();
                String xTrackingId = accountStatementResponseHeaders.get("X-Tracking-ID").get(0);
                bgwService.deleteAccountStatement(xTrackingId);

                String xmlStr = accountStatementResponse.getBody();
                importFromString(xmlStr, "BGW_C_", userId, bankAccount.getCashRegId());
            }
        }
        log.info("importSbankStatement end");
    }
}
#

i removed some code, bc i needed to hide some sensitive stuff
but u can still see the logic and the dependency between these two services

formal remnant
#

What does BGW mean?

thin basin
formal remnant
#

I see a call to bgwService.deleteAccountStatement

#

MAybe that should be in a different service that manages statements?

#

So you wouldn't have to call bgwservice?

#

This is application architecture and hard to tell with bits of code

#

I'd have to take a few hours, look at how it works and come up with a plan

thin basin
magic sleetBOT
formal remnant
#

The fact that BGWService has both a customerRepository and a paymentRepository screams bad design to me

thin basin
#

but basically id need to redesign the services and how they depend on each other, right?

formal remnant
#

Big red flags right there

#

Usually you have services depend on one single repository, for the type that they're responsible for

thin basin
formal remnant
#

And if they need to do anything else they need to go through another service

#

yeah, and not even related ones

#

bgwService.deleteAccountStatement does that call any of those repositories?

formal remnant
#

ok, other angle

#

createPaymentpaymentConfirmation

#

Why isn't that in a PaymentService?

#

And just depend on the PaymentService instead of the BGWService?

#

It's stuff like that, ask if it really belongs there