3
\$\begingroup\$

I have the following solution structure. This is a business domain (for an amount transfer operation in bank account) which will be called by a WCF service. Is the solution structuring correct?

  1. Data Acess Layer does not create domain objects. It just pass database record wrapped in another simple object (DataEntities.AccountRow). Is it a good/standard approach?

  2. Manager and domain objects are in two different layers. Is it okay?

  3. A layer “DTOforServiceCommunication” is created for communicating with WCF service.

  4. Is DTOforServiceCommunication and DataEntities are redundant or a good practice?

  5. What are the improvement points for this solution structure?

Note: The service mentioned above will be used by multiple business functions (clients). Since the SOA is not object oriented, we cannot pass business domain objects across the boundary.

enter image description here

// TransferDTO

namespace DTOforServiceCommunication
{
public class TransferDTO
{
    public int UserID { get; set; }
    public int FromAccounutNumber { get; set; }
    public int ToAccountNumber { get; set; }
    public int AmountToTransfer { get; set; }
}
}

// AccountRow

namespace DataEntities
{
public class AccountRow
{
    public int AccountNumber { get; set; }
    public string AccountType { get; set; }
    public int Duration { get; set; }
    public int DepositedAmount { get; set; }

}
}

// AccountManager

namespace BusinessManager
{
public class AccountManager
{
    public void TransferAmount(DTOforServiceCommunication.TransferDTO  transferDTO)
    {
        //DAL does not create domain objects. It just pass database record wrapped in another simple object
        DAL.AccountDAL accountDAL = new DAL.AccountDAL();
        DataEntities.AccountRow row = accountDAL.GetAcocunt(transferDTO.UserID, transferDTO.FromAccounutNumber);

        DomainObject.IBankAccount bankAccount = null;
        if (String.Equals(row.AccountType, "Savings"))
        {
            bankAccount = new DomainObject.SavingsAccount();
            bankAccount.UserID = transferDTO.UserID;
            bankAccount.AccountNumber = row.AccountNumber;
            bankAccount.AmountDeposited = row.DepositedAmount;

        }
        else
        {
                bankAccount = new DomainObject.FixedAccount();
                bankAccount.UserID = transferDTO.UserID;
                bankAccount.AccountNumber = row.AccountNumber;
                bankAccount.AmountDeposited = row.DepositedAmount;
        }

        bankAccount.Transfer(transferDTO.ToAccountNumber, transferDTO.AmountToTransfer);

    }

}
} 

//DAL

namespace DAL
{
//DAL does not create domain objects. It just pass database record wrapped in another simple object
public class AccountDAL
{

    List<DataEntities.AccountRow> dbRecords = new List<DataEntities.AccountRow>()
    {
        new DataEntities.AccountRow{AccountNumber=1,AccountType="Savings",Duration=6,DepositedAmount=50000},
        new DataEntities.AccountRow{AccountNumber=2,AccountType="Fixed",Duration=6,DepositedAmount=50000}
    };

    public DataEntities.AccountRow GetAcocunt(int userID, int accountNumber)
    {
        return dbRecords[0];
    }

}

}

READING:

  1. http://msdn.microsoft.com/en-us/magazine/dd569757.aspx

  2. https://stackoverflow.com/questions/125453/implementation-example-for-repository-pattern-with-linq-to-sql-and-c-sharp

  3. https://stackoverflow.com/questions/3175/repository-pattern-tutorial-in-c-sharp/1374420#1374420

  4. https://stackoverflow.com/questions/151769/whats-the-common-way-for-oop-pattern-design-data-access

  5. https://stackoverflow.com/questions/4198136/classic-ado-net-or-entity-framework-better-for-larger-scale-database-transaction

  6. http://sourceforge.net/projects/vanilla-dal/

  7. https://stackoverflow.com/questions/9474425/should-the-configuration-of-an-application-be-accessed-using-dal

\$\endgroup\$
4
\$\begingroup\$

Data Acess Layer does not create domain objects. It just pass database record wrapped in another simple object (DataEntities.AccountRow). Is it a good/standard approach?

Repository classes should create domain objects. The domain objects may or may not look the same as the DB entities.

If you mean that you write your DAL by yourself (and not using a ORM): Stop with that. It's a waste of time.

Manager and domain objects are in two different layers. Is it okay?

Use the term WCF service and not manager. Yes. I consider WCF services to be an UI layer since it's the interface to the calling user/client.

A layer “DTOforServiceCommunication” is created for communicating with WCF service.

The name doesn't matter as long as you understand the difference between a domain object and a DTO.

Is DTOforServiceCommunication and DataEntities are redundant or a good practice?

Good practice. The DTO's should never change since it would break all clients that use them.

What are the improvement points for this solution structure?

No need for a userId in the WCF interfaces or the DTOs unless you want to let everyone be able to look at everybody elses accounts. Use the userid provided during authentication.

Update

Repository class using vanilla ADO.NET:

public class AccountRepository
{
    Dictionary<string, Type> _accountClasses = new Dictionary<string, Type>{{"savings", typeof(SavingsAccount)}, {"fixed", typeof(FixedAccount)}};
    List<DataEntities.AccountRow> dbRecords = new List<DataEntities.AccountRow>()
    {
        new DataEntities.AccountRow{AccountNumber=1,AccountType="Savings",Duration=6,DepositedAmount=50000},
        new DataEntities.AccountRow{AccountNumber=2,AccountType="Fixed",Duration=6,DepositedAmount=50000}
    };

    public T Get<T>(int userID, int accountNumber) where T : Account
    {
        var sql = "blabla";
        using (var cmd = _connection.CreateCommand())
        {
            cmd.CommandText = sql;
            using (var reader = cmd.ExecuteReader())
            {
                if (!reader.Read())
                    return null;

                var account = CreateRow(reader);
                if (account.GetType() != typeof(T));
                    throw new InvalidOperationException("The requested account was not of the specified type");

                return account;
            }
        }
    }

    public IEnumerable<Account> FindMyAccounts(int userId)
    {
        var sql = "blabla";
        using (var cmd = _connection.CreateCommand())
        {
            cmd.CommandText = sql;
            using (var reader = cmd.ExecuteReader())
            {
                List<Account> accounts = new List<Account>();
                while (reader.Read())
                {
                    var account = CreateRow(reader);
                    accounts.Add(account);
                }

                return accounts;
            }
        }
    }        

    // IDataRecord = a row in a DataReader
    public Account CreateRow(IDataRecord record)
    {
        // might want to check so that the account got a 
        Type type;
        if (!_accountClasses.TryGetValue(record["AccountType"], out type))
            throw new InvalidOperationExcpetion("Account type do not exist: " + record["AccountType"]);

        var account = (Account)Activator.CreateInstance(record);

        // fill record here
    }

}

public class Account
{
}

public class SavingsAccount : Account
{
}

public class FixedAccount : Account
{
}

Usage:

account = _repository.Get<SavingsAccount>(1, "kdkdkdkdkd");

Update2

Added a Find method to show that the actual mapping is reused by all methods.

The Find method will also return different types of accounts in the same list.

You could also break out the ExecuteReader parts into two methods (one to fetch one item and one to fetch collections) to reuse the code more.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the clear explanation. Could you please provide links for good articles/tutorials that explains use of "Repository classes should create domain objects.". Also, are you saying that SavingsAccount/FixedAccount should be created (after checking the type) inside DAL? \$\endgroup\$ – Lijo Mar 1 '12 at 6:32
  • 1
    \$\begingroup\$ What do your DAL contain exactly? I try to follow DDD, there are several articles about the repositories in DDD. Starting point: domaindrivendesign.org \$\endgroup\$ – jgauffin Mar 1 '12 at 6:34
  • \$\begingroup\$ I have updated the post with DAL code. It will actually query from database using ADO.NET in my current code. But for simplicity it is returning from a list. \$\endgroup\$ – Lijo Mar 1 '12 at 6:37
  • 1
    \$\begingroup\$ That's in an essence a repository class. The difference is that it should use SavingsAccount and FixedAccount instead of AccountRow \$\endgroup\$ – jgauffin Mar 1 '12 at 6:41
  • 1
    \$\begingroup\$ @Lijo: I've added an example of repository \$\endgroup\$ – jgauffin Mar 1 '12 at 6:48
1
\$\begingroup\$

No, it is not. It sucked 10 yars ago and ever since LINQ and IQueryable it is just bad.

How coms you need an AccountDAL while I have not had a DAL for a specific entity for the last 15 years, awlw

Please read up on RM's and all the technology that is avaialble in .NET ever since .NET 4.0 - querying is a generic interface (naturally you need a little more code below but only once or you use one of the plenthora of prepackaged open source DAL's).

accountDAL.GetAcocunt(transferDTO.UserID, transferDTO.FromAccounutNumber);

Two mistakes here. First, who cares about the user id? Account transfers are account to account, and account numebers better are unique REGARDLESS OF USER.

So, it is:

Accounting.Get(x => x.AccountNumber == transferDto.FromAccountNumber).

Accounting is a generic repository that connects to my account indata, Get is a method implementing IQueryable and handling all loads.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks. Could you please provide a link to good articles that will explain the concepts that you explained? Also, could you please answer question 4 in my post? \$\endgroup\$ – Lijo Mar 1 '12 at 6:26
  • \$\begingroup\$ Sure. Go to microsoft, read the .NET documentation. LINQ, IQueryable are standard components for more than a year. \$\endgroup\$ – TomTom Mar 1 '12 at 9:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.