5

I have this task of modelling a system where given a group of users each can keep track of their expenses. The basic requirements are as followed:

  1. To be able to give credit to an user.
  2. Find all the transactions of a given user.
  3. Filter out the transaction where either user is borrower or spender.
  4. Create a group and put users in that group and then when a group is mentioned then distribute money equally among them.[TODO]

I have tried few things but I am not convinced at all.

const trxn = new Array();

class Transaction {
    constructor(amount, from, to) {
      this.timestamp = new Date();
      this.amount = amount;
      this.from = from;
      this.to   = to;
      trxn.push(this);
    }

    toString() {
      let dd = this.timestamp.getDate();
      let mm = this.timestamp.getMonth();
      let yy = this.timestamp.getFullYear();
      return `[Transaction: ${dd}/${mm}/${yy}] ${this.from} Gives ${this.amount} to ${this.to}`;
    }
}

class Person {
    constructor(name) {
      this.name = name;
      this._transactions = [];
    }

    transfer(amount, to) {
      let t = new Transaction(amount, this, to);
      this._transactions.push(t);
    }

    transactions() {
      return this._transactions;
    }

    toString() {
      return `${this.name.charAt(0).toUpperCase() + this.name.slice(1)}`;
    }
}


function history(transactions, user) {
  return transactions.filter((t) => {
    return t.to.name === user.name || t.from.name === user.name;
  });
}


let amy = new Person('amy');
let foo = new Person('foo');
amy.transfer(500, foo);
amy.transfer(500, foo);
foo.transfer(200, amy);

for (let t of amy.transactions()) {
  console.log('Transaction by AMY');
  console.log(t);
}

for (let f of foo.transactions()) {
  console.log('Transaction by FOO');
  console.log(f);
}

console.log('Transaction history');
console.log(history(trxn, amy));

Output of the above logic:

Transaction by AMY
[Transaction: 22/10/2016] Amy Gives 500 to Foo
Transaction by AMY
[Transaction: 22/10/2016] Amy Gives 500 to Foo
Transaction by FOO
[Transaction: 22/10/2016] Foo Gives 200 to Amy
Transaction history
[Transaction: 22/10/2016] Amy Gives 500 to Foo,
[Transaction: 22/10/2016] Amy Gives 500 to Foo,
[Transaction: 22/10/2016] Foo Gives 200 to Amy

How to approach problems like these? I have read that just think about the business logic and not about the implementation details hence in my code there is no mention of delivery mechanism and all.

How can I make my code more flexible and robust following the OOP practices?

CodeYogi
  • 2,156
  • 1
  • 18
  • 34
  • see [Are there any OO-principles that are practically applicable for Javascript?](http://softwareengineering.stackexchange.com/q/180585/31260) – gnat Nov 22 '16 at 08:46
  • @gnat that seems a great link but I am more interested in design side too. – CodeYogi Nov 22 '16 at 08:59
  • @CodeYogi I'd take a look at the Event Sourcing pattern, it is very apt for accounts and transactions.. – Adrian Thompson Phillips Nov 25 '16 at 10:40
  • A transaction has two dates: when it's issued and when it arrives at the other side. This is often ignored (and the banks happily shovel the money in their cellar). –  Nov 30 '16 at 12:01

2 Answers2

3

This sounds like the standard bank account double book accounting problem. Don't be fooled by all the talk of Users, the objects you are modelling are accounts and they have transactions.

whenever you do a debit or credit you create two transactions. ie Amy gives Beth 100 dollars

Id,AccountId,Value
1,Amy,-100
2,Beth, +100

If you like you can add an extra linking Id to provide the 'where does the money come from' info

Id,AccountId,Value,LinkId
1,Amy,-100,1
2,Beth, +100,1
Ewan
  • 70,664
  • 5
  • 76
  • 161
1

The main design problem I can see is this

Transaction by AMY

In order to get to your problem you will also need a variable to save the amount of money a Person has. Foo was in this transaction, it should out put Transaction between AMY and FOO, in order to get this out, you need to remove the Transaction from the class Person, maybe giving an ID to every Person and every time you make a new Transaction it could look like this :

Transaction(Sender, Receiver, Amount)

t = new Transaction(4, 2, 500); //Person with ID 4 transfers 500 to Person with ID 2
transactions.push(t);

In order to make things simpler you should have a money field in your Person class. I'm not too sure what you mean by :

Create a group and put users in that group and then when a group is mentioned then distribute money equally among them

But to my comprehension you want to make a group split up their money like this :

P1 has $500

P2 has $400

P3 has $300

And you want to make P1 transfer $100 to P3 in order to distribute the money equally (that's what I understand). for that you need to get the average (in this case 400) and loop over everyone above the average and transfer money down.

Here's another case :

P1 has $8

P2 has $7

P3 has $4

P4 has $1

In this case the average is $5, so P1 needs to transfer to P4, but he can only transfer $3 to get to the average so he transfers $3 to P4. Now it looks likes this :

P1 has $5

P2 has $7

P3 has $4

P4 has $4

Now since P1 was looped over P2 ($7) transfers to P4, but only $1 to reach the average of $5, and then since P2 has $6, we got to P3 and transfer him $1. So the transactions went like this :

P1 transferred to P4 $3

P2 transferred to P4 $1

P2 transferred to P3 $1

Hopefully, this helps you out!

Cedric Martens
  • 219
  • 1
  • 9