SOA Question: Exposing Entities
Asked Answered
D

5

2

I would like to incorporate SOA pattern in my 3 tier structure. I created a Service layer (WCF Host) between the BLL and the UI. My structure setup is now looks like this

UI <> WCF <> BLL <> DAL

   <---[Entities] --->

The problem is, I have my entities in separate DLL (ANd it was visible in ALL Layers except on the UI) Now, I need to expose it so that the consumer of my service can use it.In this case, the UI. How can I possibly do that?

Entities.DLL

   namespace Entities 
   {
       public class Account
       {
           public string AcctID { get; set; }
           public string AcctName { get; set; }
       }
    }

now, Im planning to use it in WCF

Service Interface Layer

    public class AccountService : IAccountService
    {

         public Account GetAccount(string AcctID)
         { 
             //fetch  from DAL through BLL
          }
     }

Is it ok to just Attribute my Entities? (Note, I'm also using the entities in DAL and BLL)

  using System.Runtime.Serialization;
   namespace Entities 
   {
      [DataContract]
       public class Account
       {
          [DataMember]
           public string AcctID { get; set; }

          [DataMember]
           public string AcctName { get; set; }
       }
    }

Any suggestion guys?

Devonne answered 7/10, 2010 at 15:18 Comment(0)
C
3

Here's the system that works for us:

You should typically use a Data Transfer Object that reflects the data you're expecting to need on the client side. The Business Layer should define these DTOs, along with their repository interfaces. The Data Layer should implement the repository interfaces, converting your data-layer entities into the DTOs. The WCF layer should simply be an outward-facing wrapper for your various repository interface methods.

This way, it looks more like this:

 UI ---\ 
  |     BLL   --   DAL
 WCF---/
    [     DTO    ]
    [Repositories]
                 [Entities]

In my mind, I see the WCF layer as being a part of the UI layer, so I'd feel all right having them both be aware of objects defined in the business layer. However, you could take it one step further, and make the WCF layer be in charge of converting your business objects into DTOs:

 UI   --   WCF   --   BLL   --   DAL
 [    DTOs   ]
         [      Repositories      ]
         [    Business Objects    ]
                              [Entities]

This way, each layer is only aware of at most a single layer on each side of it. The DTOs can be annotated for serialization or whatever, because they really are only intended for that purpose. Only the Data-Access Layer is aware of your Data Entities.

In Response to your comment:

If your entities are defined in your data-access layer, then they really are not DTOs. They are modeling your data layer, which doesn't necessarily translate directly into the objects you need in the UI.

The reason I'm suggesting defining interfaces for your repositories is so that you can use dependency injection to loosen the coupling between your WCF layer and your business layer. This will also serve to make your WCF layer unit-testable, because you can create fake or mock repository implementations that simulate a particular situation.

In the first model I recommended, your WCF methods would look almost exactly like your repository methods, so a typical WCF would basically just "wrap" a repository method:

public IEnumerable<Task> GetActiveTasks() {
    return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser);
}
Clearly answered 7/10, 2010 at 15:28 Comment(5)
Thanks stripling for the detailed illustration. I havent tried the mapping of Entity to DTO approach so i have lots of study to do and I think thats the way to go. But I will just focus on DTO and Entity, the ENyity is visible to the BLL and DAL and the DTO will be used in transfering of data from WCF to UI. The WCF will handle the mapping..Devonne
That's a good place to start. I know it sounds redundant to have DTOs and Entities that look almost exactly the same, but it pays off if your project ends up getting very big.Clearly
can you incorporate the repository on my Answer? where would i put it? thanks sir..Devonne
@Clearly Could you please answer #9481369 ?Cyrano
@Lijo: I didn't add an answer because I agree with Mark Seeman's response.Clearly
D
1

I can tell you how I do it.

I have separate DTO's and Entities - and it is not always a 1:1 relationship. I really don't like to have all the attributes in my entities. (Also, it breaks encapsulation as all properties are suddenly read-write.

If you want easy conversion between the two - there are libraries to make it easy(ier) like AutoMapper.

If you use the entities as DTOs you will often send way too much data - e.g. an Order having an Account having multiple OpenOrders of type Order. Everytime you fetch one order, you will get all the open orders of the Account as well.

Secondly - I would use the same business-dll on the UI as I use in the service layer - so I can validate on the client-side before sending it to the server. This part is optional of course - you could also duplicate the logic (but I hate duplication as well :-)).

Hope this gets you a bit of direction.

Doradorado answered 7/10, 2010 at 15:28 Comment(2)
sounds redundant to me (mapping ENtity-DTO) and also additional coding.. Is there any way combining DTO and ENtity as one?Devonne
It isn't redundant - it's separation of concerns - having all responsibilities in one big lump is a sure way to create a maintenance nightmare.Doradorado
D
1

I think I Got IT using the AutoMapper.

I Finally expose the Entity as a DTO via WCF ..the simple way..

the Entity

 namespace Entities
 { 
   public class Account
   {
    public string AccountNo { get; set; }
    public string AccountName { get; set; }
   }
 }

BLL

 namespace BLL
 {
   // This defines the repository interface.
   public interface IAccountRepository
   {
     Account GetAccount(int accountId);
   }

   public class AccountRepository
   {
     public Account GetAccount(int accountId) {
       // get the Account object from the data layer.
     }
   }

   // Using an interface makes it easy to swap various implementations.
   // The implementation above would be the one you'd normally use, but you could
   // create others like this to help with unit testing and such.
   public class FakeAccountRepository : IAccountRepository
   {
    public Account GetAccount(int accountId)
    {
        return new Account { AccountName = "JuanDeLaCruz", AccountNo = "123456" };
    }
   }
 }

WCF

[ServiceContract]
public interface IService
{
   [OperationContract]
   AccountDTO GetAccount(int accountId);
}


 [DataContract]
 public class AccountDTO
 {
  [DataMember]
  public string AccountNo { get; set; }
  [DataMember]
  public string AccountName { get; set; }
 }


 public class Service : IService
 {
   // Define a Factory in your .svc file to inject a repository implementation.
   // It's best to use an IoC container like Ninject for this sort of thing.
   public Service( // no pun intended
       IAccountRepository accountRepository)
   {
     _accountRepository = accountRepository
   }

   public AccountDTO GetAccount(int accountId)
   {
     Mapper.CreateMap<Account, AccountDTO>();
     var account = _accountRepository.GetAccount(accountId);
     var accountDto = Mapper.Map<Account, AccountDTO>(account);
     return account;
   }
 }

WCF Aspx Consumer

 protected void Page_Load(object sender, EventArgs e)
 {
    ServiceClient accountService= new ServiceClient();
    AccountDTO account = accountService.GetAccount();
    Response.Write(account.AccountName);
 }

Please comment for any suggestions/corrections guys.. ^^

THANKS to Sir Stiffling and Goblin

Devonne answered 7/10, 2010 at 16:40 Comment(3)
As requested, I modified this to include the repository interface.Clearly
Seems sound to me :-) Just one detail - you might want to only run Mapper.CreateMap<Account, AccountDTO>(); once. It is either just expensive to create it everytime - or it throws the second time you call it.Doradorado
Could you please answer #9481369 ?Cyrano
S
1

DTOs are very good approach and in some scenarios they are absolutely necessary. Some of these scenarios are:

  • Big projects - goes together with other scenarios. Separation of Concerns is important.
  • Entities are domain objects = contains business logic
  • Entities are somehow .NET specific and service has to be used from other platforms
  • Service layer exposes specialized data types for each operation instead of CRUDy interfaces. For example operation for selecting can return object with data like creation date, last modification date, etc. But operation for updating does not need to transfer this data from client so it uses DTO whithout these fields. You usually go even further and you don't have pure selecting and updating but some real business functions.

On the other hand your architecture should be driven by your requirements and expected complexity and size of your application. DTO involve a lot of additional work = additional costs. For smaller simple project where your service will be consumed only by your UI client written in .NET there is nothing wrong in defining your entities in separate assembly, marking these entities with attributes for serializing and with data annotations (can be used for validation on both client and server side) or with other validation attributes (for example Validation application block from Enterprise library) and sharing this assembly among all application layers including UI client. Simplicity first.

Steelworker answered 7/10, 2010 at 19:51 Comment(0)
B
0

If you want to expose service contract on the client the simpliest way is this

[Serializable]
public class Accountd
{
    public string AccountNo { get; set; } 
    public string AccountName { get; set; } 
}

and if you need more control on which member should cross the boundary then use this

[DataContract]
public class Account
{
    [DataMember]
    public string AccountNo { get; set; } 
    public string AccountName { get; set; }  **// client won't see this data.**
}
Bawcock answered 7/10, 2010 at 23:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.