I once implemented a similar function, hope it helps :)
Our organization owns an online-payment handling application. Once a customer finishes payment, the online-payment provider sends use a notification indicating success or failure. Somtimes network failures occur, the notification may never reach our application. hence, here comes disgruntled customers. So an auto-checking mechanism is needed.
An application runner is responsible for keeping the check running:
public class CheckingBatch {
private TransactionChecker transactionChecker;
public void run() {
List<Transaction> transactions = transactionsToBeChecked();
for (Transaction transaction : transactions) {
//publish events if the transaction needs check
doCheck(transaction, now); }
}
}
private List<Transaction> transactionsToBeChecked() {
return transactionRepository.findBy(transactionChecker
.aToBeCheckedSpec());
}
}
Annother application service listens the event and do the actual check:
public class CheckTransactionServiceImpl implements CheckTransactionService {
private TransactionChecker transactionChecker;
@Transactional
public void check(final TransactionNo transactionNo) {
Transaction transaction = transactionRepository.find(transactionNo);
CheckResult result = transactionChecker.check(transaction);
//handle check result
}
}
The TransactionCheck is an online-payment-solution agnostic domain service:
public interface TransactionChecker {
/**
*
* | data between online-payment provider and ours | txn STATUS | RESULT |<br>
* | consistent | CLOSED | VALID |<br>
* | inconsistent | CLOSED | INVALID |<br>
* others omitted
*/
CheckResult check(Transaction transaction);
/**
* returns txn specification to filter to be checked ones.
*/
ToBeCheckedSpecification aToBeCheckedSpec();
}
As you may see, both the application service and domain service are now stateless.
IMHO, Ping is a domain service(correlate to TxnChecker), but the monitor is kind of an application service(correlate to CheckingBatch).