Untitled

mail@pastecode.io avatar
unknown
java
2 years ago
9.5 kB
4
Indexable
Never
package mwolczecki;

/*

The idea of this coding challenge, is to implement a service able to subscribe to upstream
balance change notifications from the GeneralBalanceService, keep an accumulated running balance
for each customer, and send downstream balance update notifications for subscribed customers.

Steps:
1. Subscribe to balance changes for all customers by providing a GeneralBalanceUpdateCallback
   to the GeneralBalanceService instance received on the constructor.
2. Update internal data-structure to hold balances for each customer when the
   GeneralBalanceUpdateCallback::onBalanceUpdate method is called.
3. If the customer has a registered callback provided via the CustomerBalanceService::subscribe
   method, call the CustomerBalanceUpdateCallback::onBalanceUpdate method with an instance of
   CustomerBalanceUpdate.

+-----------+                                    +-------------------------+                            +-----------------------+
| Customer  |                                    | CustomerBalanceService  |                            | GeneralBalanceService |
+-----------+                                    +-------------------------+                            +-----------------------+
      |                                                       | -------------------------------\                    |
      |                                                       |-| ::new(GeneralBalanceService) |                    |
      |                                                       | |------------------------------|                    |
      |                                                       |                                                     |
      |                                                       | subscribe(GeneralBalanceUpdateCallback)             |
      |                                                       |---------------------------------------------------->|
      |                                                       |                                                     |
      | subscribe(CustomerBalanceUpdateCallback)              |                                                     |
      |------------------------------------------------------>|                                                     |
      |                                                       |                                                     |
      |                                                       |                                                     |
      |                                                       |                                                     |
      |                                                       |                                        -----------\ |
      |                                                       |                                        | customer |-|
      |                                                       |                                        | buy/sell | |
      |                                                       |                                        | crypto   | |
      |                                                       |                                        |----------| |
      |                                                       |                                                     |
      |                                                       |                        GeneralBalanceUpdateCallback |
      |                                                       |             ::onBalanceUpdate(GeneralBalanceUpdate) |
      |                                                       |<----------------------------------------------------|
      |                                                       | ------------------\                                 |
      |                                                       |-| update internal |                                 |
      |                                                       | | balances data   |                                 |
      |                                                       | | structure       |                                 |
      |                                                       | |-----------------|                                 |
      |                                                       |                                                     |
      |                         CustomerBalanceUpdateCallback |                                                     |
      |              ::onBalanceUpdate(CustomerBalanceUpdate) |                                                     |
      |<------------------------------------------------------|                                                     |
      |                                                       |                                                     |

*/

import java.util.*;
import java.util.stream.Collectors;

enum Token {
    USD, EUR, BTC, ETH;
}

/**
 * Message/Event received from the upstream GeneralBalanceService,
 * for any change in the balance of any token for any customer
 * Balance Increases if getChange() > 0
 * Balance Decreases if getChange() < 0
 */
interface GeneralBalanceUpdate {
    long getCustomer();

    Token getToken();

    double getChange();
}

/**
 * A callback provided to the upstream GeneralBalanceService, to be called
 * whenever there is a change in the balance of a customer for any token
 */
interface GeneralBalanceUpdateCallback {
    void onBalanceUpdate(GeneralBalanceUpdate update);
}

/**
 * Service to subscribe for updates in the balances of all customers
 * (provided)
 */
interface GeneralBalanceService {
    void subscribe(GeneralBalanceUpdateCallback callback);
}

/**
 * A callback provided to the CustomerBalanceService, to be called
 * whenever there is a change in the balance of a token for the subscribed customer
 */
interface CustomerBalanceUpdateCallback {
    void onBalanceUpdate(CustomerBalanceUpdate update);
}

/**
 * Message/Event sent to the downstream customer balance subscription, whenever
 * the balance for a token for that customer changes
 */
class CustomerBalanceUpdate {

    private final Token token;
    private double amount;
    private double change;

    public CustomerBalanceUpdate(Token token, double amount, double change) {
        this.token = token;
        this.amount = amount;
        this.change = change;
    }

    public Token getToken() {
        return token;
    }

    public double getAmount() {
        return amount;
    }

    public double getChange() {
        return change;
    }
}

/**
 * A Service used by customers to get notifications of changes
 * to their own specific balances, for each {@link Token}
 * (implement)
 */

class CustomerBalanceService implements GeneralBalanceUpdateCallback {

    private final GeneralBalanceService balanceService;
    private final Map<Long, CustomerDetails> customers;

    public CustomerBalanceService(GeneralBalanceService balanceService) {
        this.balanceService = balanceService;
        customers = new HashMap<>();
        balanceService.subscribe(this::onBalanceUpdate);
        // TODO: subscribe to GeneralBalanceService notifications
    }


    private void updateBalance(long customer, Token token, double change) {
        // TODO: update in-memory data structure to reflect the received balance change
    }

    public void subscribe(long customer, CustomerBalanceUpdateCallback callback) {
        if (!customers.containsKey(customer)) {
            Map<Token, Double> collect = Arrays.stream(Token.values()).collect(Collectors.toMap(i -> i, i -> 0d));
            customers.put(customer, new CustomerDetails(List.of(callback), collect));
        } else {
            CustomerDetails customerDetails = customers.get(customer);
            List<CustomerBalanceUpdateCallback> customerBalanceUpdateCallbacks = new ArrayList<>(customerDetails.callbacks);
            customerBalanceUpdateCallbacks.add(callback);
            customers.put(customer, new CustomerDetails(customerBalanceUpdateCallbacks, customerDetails.currencyData));
        }
        // TODO: store the callback provided by the customer, so it can be triggered when a change is received
    }

    @Override
    public void onBalanceUpdate(GeneralBalanceUpdate update) {
        long customer = update.getCustomer();
        if (!customers.containsKey(customer)) {
            Map<Token, Double> collect = Arrays.stream(Token.values()).collect(Collectors.toMap(i -> i, i -> 0d));
            customers.put(customer, new CustomerDetails(Collections.emptyList(), collect));
        }

        CustomerDetails customerDetails = customers.get(customer);
        Map<Token, Double> currencyData = customerDetails.currencyData;

        currencyData.put(update.getToken(), currencyData.get(update.getToken()) + update.getChange());
        CustomerBalanceUpdate customerBalanceUpdate = new CustomerBalanceUpdate(update.getToken(), currencyData.get(update.getToken()), update.getChange());
        customerDetails.callbacks.forEach(i -> i.onBalanceUpdate(customerBalanceUpdate));
    }

    record CustomerDetails(List<CustomerBalanceUpdateCallback> callbacks, Map<Token, Double> currencyData) {
    }


}