Reputation: 17772
Given a structure for say, a bank account..
class Account
{
virtual int Id { get; set; }
virtual int Balance { get; set; }
}
And I want to track transactions done, so say a simple class...
class Transaction
{
virtual int Id { get; set; }
virtual Account Account { get; set; }
virtual DateTime Timestamp { get; set; }
virtual int Amount { get; set; }
}
Let's assume I want to keep track of transactions done, which is the more intelligent approach here?
interface IAccountRepository
{
void Deposit(int account, int amount)
}
or ...
class Account
{
void Deposit(int amount)
{
// this one is easier, but then I have to repeat
// myself because I need to store the Transaction
// in the database too.
}
}
The Repository pattern seems to be the most encompassing, since it will have a handle to the unit of work/orm/session (using nHibernate) - but using a class-level method seems more straightforward, since it's more in line with standard object oriented principle of 'Do this to this object'.
My question is that if I want to log the transactions, then I have to make sure they get saved as database objects too. Going the second route, with a class level method, I can't do this inside of the Account
class, so I would end up having to repeat myself.
My other option is another abstraction..
interface ITransactionRepository
{
void CreateTransaction(int account, int amount);
}
Which works fine, It kind of wraps A and B together because I would find the account in the TransactionRepository
and then perform its Deposit
method, but it doesn't really feel like this is a wise approach. I don't know why, my gut just tells me it isn't the best way to go.
This applies to more than just this one set of classes, of course - it's a principle of design. I wanted to see what more veteran programmers would do in this situation, if you have any thoughts.
Upvotes: 1
Views: 235
Reputation: 18034
Your question is a nice example of problems that arise from the structural DB / OO mismatch. In my experience, people tend to favor the first option you described - bypass the Account business object and store the Transaction in the DB by using the repository.
I strongly recommend not to let your DB structure influence the business layer design. Business objects are there to implement business behavior. When you access the repository directly for an operation that belongs to the Account class from a business point-of-view, your business rules will end up at a strange location - somewhere outside the Account class.
To answer your question, the approach that I always take in these situations is as follows:
Adhere to OO principles in your business logic. Structure your classes and interactions according to real-world processes, so business rules end up where you'd expect them.
Provide a storage-oriented view to the DB. What I mean with this is that your repository needs not mimic the structure of your business objects. For example, implement an repository that does CRUD for accounts and the transactions.
Upvotes: 0
Reputation: 50018
I would sugguest using the repository pattern for CRUD (Create, read, update, delete) operations on the Accounts.
interface IAccountRepository
{
Add(Account acc);
Remove(Account acc);
Account GetAccountById(int account);
Update(Account acc);
}
Then put the Deposit method in the Account class like you mentioned
class Account
{
void Deposit(int amount)
{
}
}
Then you access the account through the repository to update the account
// Get the account by id
Account acc = rep.GetAccountById(23143);
// Deposit the money
acc.Deposit(21.23);
// Update if needed
rep.UpdateAccount(acc);
Transations could be done in a similar way, but I would probably store the account id, rather than the Account instance in the Transcation.
class Transaction
{
virtual int Id { get; set; }
virtual int AccountId { get; set; }
virtual DateTime Timestamp { get; set; }
virtual int Amount { get; set; }
}
Upvotes: 1