Simple authorization handler created for Nubank
This architecture proposal for an authorization handler was based on some premises:
- Isolation of the business logic (provide good test coverage)
- Extension (to add new rules and violations)
- Easy maintenance
To fulfill the first premise, I thought I could implement the Repository Pattern. In this way, it was possible to separate the data access logic from the domain logic. This would be concentrated in a layer called Service. I tried in some way to apply the bounded context (DDD) to isolate the domains and I believe I've arrived a reasonable result. I believe that architectures are evolutionary and the proposed structure could at some point receive other layers to accompany the needs and growth of the application.
It was interesting to take the challenge of developing the project in clojure. I am not proficient in that language and yet I realized its benefits. The agility in development and the ease of refactoring were often notorious. These points were only achieved by the guarantee that the unit tests brought. In this way it was possible to maintain a good code coverage (90%>) throughout the development.
This script uses the same environment variables as libpq to connect to a PostgreSQL server.
docker build -t authorizer .docker run -i authorizer < operationsThis script uses the same environment variables as libpq to connect to a PostgreSQL server.
lein run < operationsThis script uses the same environment variables as libpq to connect to a PostgreSQL server.
lein uberjarjava -jar target/authorize-standalone.jar < operationsThis script uses the same environment variables as libpq to connect to a PostgreSQL server.
lein binbin/authorize < operationsAre a set of rules that transactions must satisfy to be captured.
Check if account was previously initialized. Return "account-already-initialized" if true.
{ "account": { "activeCard": true, "availableLimit": 100 } }
{ "account": { "activeCard": true, "availableLimit": 100 } }
{ "transaction": { "merchant": "Bullguer", "amount": 10, "time": "2019-02-13T11:00:00.000Z" } }docker run -i authorizer < operations{"account":{"activeCard":true,"availableLimit":100},"violations":[]}
{"account":{"activeCard":true,"availableLimit":100},"violations":["account-already-initialized"]}
{"account":{"activeCard":true,"availableLimit":90},"violations":[]}
Check if card is active at account entity. Return "card-not-active" if true and and block the transaction from been captured.
{ "account": { "activeCard": false, "availableLimit": 100 } }
{ "transaction": { "merchant": "Starbucks", "amount": 10, "time": "2019-02-13T11:00:00.000Z" } }docker run -i authorizer < operations{"account":{"activeCard":false,"availableLimit":100},"violations":[]}
{"account":{"activeCard":false,"availableLimit":100},"violations":["card-not-active"]}
Check if there were two or more transactions with the same merchant and amount in a time window (2 minutes). Return "doubled-transaction" if true and block the transaction from been captured.
{ "account": { "activeCard": true, "availableLimit": 100 } }
{ "transaction": { "merchant": "Cuor di Crema", "amount": 10, "time": "2019-02-13T11:00:00.000Z" } }
{ "transaction": { "merchant": "Cuor di Crema", "amount": 10, "time": "2019-02-13T11:00:00.000Z" } }
{ "transaction": { "merchant": "Cuor di Crema", "amount": 10, "time": "2019-02-13T11:00:00.000Z" } }docker run -i authorizer < operations{"account":{"activeCard":true,"availableLimit":100},"violations":[]}
{"account":{"activeCard":true,"availableLimit":90},"violations":[]}
{"account":{"activeCard":true,"availableLimit":80},"violations":[]}
{"account":{"activeCard":true,"availableLimit":80},"violations":["doubled-transaction"]}
Check if there were three or more transactions in a time window (2 minutes). Return "high-frequency-small-interval" if true and block the transaction from been captured.
{ "account": { "activeCard": true, "availableLimit": 100 } }
{ "transaction": { "merchant": "Lojas Americanas", "amount": 10, "time": "2019-02-13T11:00:10.000Z" } }
{ "transaction": { "merchant": "Lojas Americanas", "amount": 10, "time": "2019-02-13T11:00:43.000Z" } }
{ "transaction": { "merchant": "Submarino", "amount": 10, "time": "2019-02-13T11:00:55.000Z" } }
{ "transaction": { "merchant": "Ricardo Eletro", "amount": 10, "time": "2019-02-13T11:00:59.000Z" } }
{ "transaction": { "merchant": "Kalunga", "amount": 10, "time": "2019-02-13T11:01:11.000Z" } }
{ "transaction": { "merchant": "Kalunga", "amount": 10, "time": "2019-02-13T11:05:11.000Z" } }docker run -i authorizer < operations{"account":{"activeCard":true,"availableLimit":100},"violations":[]}
{"account":{"activeCard":true,"availableLimit":90},"violations":[]}
{"account":{"activeCard":true,"availableLimit":80},"violations":[]}
{"account":{"activeCard":true,"availableLimit":70},"violations":[]}
{"account":{"activeCard":true,"availableLimit":70},"violations":["high-frequency-small-interval"]}
{"account":{"activeCard":true,"availableLimit":70},"violations":["high-frequency-small-interval"]}
{"account":{"activeCard":true,"availableLimit":60},"violations":[]}
Check if available amount is bigger than the transaction amount. Return "insufficient-limit" if true and block the transaction from been captured.
{ "account": { "activeCard": true, "availableLimit": 60 } }
{ "transaction": { "merchant": "Tok&Stok", "amount": 50, "time": "2019-02-13T11:00:10.000Z" } }
{ "transaction": { "merchant": "Bullguer", "amount": 13, "time": "2019-02-13T11:00:43.000Z" } }
docker run -i authorizer < operations{"account":{"activeCard":true,"availableLimit":60},"violations":[]}
{"account":{"activeCard":true,"availableLimit":10},"violations":[]}
{"account":{"activeCard":true,"availableLimit":10},"violations":["insufficient-limit"]}
Run all unit tests
lein midje