Tech Off Thread

18 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

.Net 2.0 Winform Architecture Help

Back to Forum: Tech Off
  • User profile image
    ferguslogic

    Okay guys,

    I am re-writing an application in C# .NEt 2.0 right now and I could use some architecture advice. 

    Our deployment scenario.
    ===============================

    The application will be deployed to small businesses across the U.S. At each business location the system could be ran on 1 standalone PC or on 5, 8 or 10 PCs simultaneously on the same LAN.  Very simple small network deployment.

    Front end is C# and database is sql express 05.


    The architecture that i initially developed is listed below. Each layer communicates only with the layer below it and there is one serializable layer that can be marshalled across all layers to pass data easily through any layer.

    3 basic layers
    ================
    UI  - all UI related logic (communicates with Business)
    Business - validation, other business logic (communicates with data)
    Data - communicates directly with sql server

    serializable layer can be marshaled to any layer..referenced by all =====================
    Entity
      contains basic classes company, order, user  etc.



    So my method behind the madness here was this

    1) UI will call business function i.e GetCustomer()
    2) business tier will call data tier GetCustomer()
    3) data will return customer data to business
    4) business will handle any necessary logic and send customer entity to UI for display

    UI will utilize OO customer entity.



    Yeahh...well it was a nice thought. In actual application however this is not working well. Why? Because I am having to make 2-5 round trips to the database per winform screen and that is the problem i want to solve.

    Example scenario

    Lets say we have a winform screen.
    Lets say it displays inventory.

    The screen contains 1 inventory object, a listing of manufacturers and a listing of vendors.

    2 resultsets/collections and 1 class (inventory) are needed for this screen to populate correctly.

    If i use custom entities (classes and collectiosn)
    =====================================
    with my current design the UI would need to call 3 business functions called GetVendors(), getManufacturers() and getInventory  to populate vendor, manufacturer and inventory classes/collections to return all the data needed to the front end.

    Isn't this too many roundtrips to the database here?

    If I use a Dataset
    ================================
    I can return all the data to the front end at one time but then i loose my object oriented class structure and isdirty flags etc. and I am left with a dataset on the front end that has 3 tables manufacturers vendors and inventory.




    The answer to this problem seems to be this

    1) UI calls a business function called GetInventoryScreen()
    2) business calls Data function GetInventoryScreen()
    3)data tier returns dataset to business tier with all data needed
    4)business tier turns dataset into OO classes/collections
    5) OO classes/collections returned to UI for display.

    This solves all problems 1 round trip and I get to use OO objects up front.

    The problem with this design is that my data tier has methods called GetInventoryScreen() which means that it is tied directly to the UI.

    What I would like is to make as few round trips as possible while using OO objects in the UI rather than Datasets if possible and without having the data tier contain functions that are based on the UI.





  • User profile image
    JohnAskew

    I must have missed the point.
    It seems all you would need/want to do is rename the GetInventoryScreen() function in your data layer to something not "tied to the UI". You've already re-written it to provide the correct data according to those 5 steps...

    ?

  • User profile image
    ferguslogic

    If this is the resolution, to create 1 method that returns an all emcompassing dataset with manufacturers, vendors and inventory to reduce the round trips to 1 round trip  then the problem becomes:
     
    Where do you place the business and data functions if they no longer return just inventory data but also return manufacturers and vendors.  Right now my business layer and data layer map directly to the tables for the most part. So they look like this

    biz layer
    ==========
    customerbiz   -handles logic for customers
    orderbiz   - handles logic for orders
    inventorybiz -  handles logic for inventory
    manufacturerbiz - handles logic for manufacturers
    vendorbiz - handles logic for vendors

    data layer
    =============
    customerDAL - CRUD for customers
    orderDal - CRUD for orders
    inventoryDal - CRUD for inventory etc.
    ManufacturerDal - CRUD for Manufacturers
    vendorDal - CRUD for Vendors


    Today the architecture would flow like this if i was just returning inventory data by itself in a very simplistic non-real world scenario:

    Inventory Screen
    --which calls
    Inventory business object  GetInventoryItem()
    ---which calls
    Inventory Data Layer   getInventoryItem()
    ---which contacts database to return inventory data


    but now how would it flow if you are returning inventory, vendors and manufacturers all at once to support a real-world winform screen?

    Inventory Screen

    which calls

    some function in some business object 
    (not sure where this function would go now or what to call it)

    which calls

    some function in data object
    (not sure where this function would go now or what to call it)

    which returns data from database


    Now that you have a function that returns manufacturers, vendors and inventory it does not fit into the inventory, vendors or manufacturer business or data objects because it is no longer returning 1 type of data.

    So what business object do you place this new function in and what data layer object to you place it in as well?


    Or

    Should I even worry that i am making 3 round trips on a LAN?  Is the speed possibly negligible if the customer is running a 100 Mb network.

    Perhaps I should just call
    GetManufactuers()
    GetVendors()
    GetInventory

    and make 3 round trips and be done with it.  Is it possible that this is the best solution?



  • User profile image
    Shagate

    Think about "responsibility", not functionality.
    (I still struggle with this myself sometimes.)

    There is no such thing as a "perfect" model for all applications. The folks a Pattern's and Practices give some great examples of how to write fully extensible applications.

    Everyone has a different idea of how to approach problems, but here's my "big" solution. Depending on the scope and duration of the project I would certainly combine layers for maintenance, etc.

    PS: Change the DAL to something more generic if you're not SQL specific.

    Example:

    SQL-DB:    Inventory, Customer, Order Tables
    ----
    DAL-CMD: Static SqlCommand Objects
    DAL-MAP:  SqlCommand / DataEntity Mapping
    DAL-ENT:  Data Entities
    DAL-MAN: Data Manager
    DAL-DOM: Data Domains
    ----
    BUS-ENT: Business Entities
    BUS-MAN: Business Manager
    BUS-WF: Business Workflow
    BUS-CMP: Business Components
    ---
    UI-PROC: UI Processes
    UI-DISP: UI Display

    Okay, now for the long winded explaination...

    DAL-CMD
    These are SqlCommand objects pointing to stored procedures in your database. These could also be simple SQL calls if you want. They are static, so any data object can grab one at any time. I wrote a simple code generator to create this class from my database.

    DAL-MAP
    This class contains functions that map one-to-one to the static SqlCommand objects in DAL-CMD. The methods consume a Data Entity class (Orders for example) and map the properties to the SqlCommand object. You could also create methods that consume parameters. Again, I use a code generator to create this class directly from the database tables.

    DAL-ENT
    This class contains Data Entities which are created based on your Database Tables. They have static members which reference the DAL-MAP CRUD operations through interfaces.  This way the DAL-MAN can execute generic commands. Again, these classes are auto-generated from a script.

    DAL-MAN
    The Data Manager is responsible for executing CRUD commands and Transaction support. Think of it as a connection gatekeeper. It will invoke the DAL-ENT's Update, Delete, Insert, etc. interface methods. Need Transactions? Pass an array of DAL-ENT's to it.

    DAL-DOM
    This is the part you're interested in. This class (or these classes) manage result sets, and break them down into their Data Entities. So in your case, define a class (CustomerInventory) with the constructor you need (InventoryId, CustomerId, OrderId) that will ask for a DataSet with 3 tables returned from a single batch command (only 1 SQL call), and define a set of TableAdapters to map to each table. It will then pass the DataRows to the DAL-ENT class to create instances of it in an array/collection and hold a reference. Your BUS-MAN class can then create BUS-ENT from the DAL-DOM class you created.

    BUS-ENT
    Business Entities contain the same basic information as a Data Entity, but with additional data more relevant to your application's needs (not the database's). Also, I believe Pattern's and Practices suggests using a separate Translation class to map Data Entities and Business Entities together. It's your call...

    BUS-MAN
    The Business Manager(s) and Data Manager talk together to form a solution. The respective Entity classes are there to act as containers for information and basic functionality. I would use a 'manager' to handle envoking a data operation and the translation of a Business Entity into a Data Entity and visa-versa. Similar object types should be grouped together under a single manager.

    BUS-WF
    These classes manage the workflow processes (and possibly state) for your application. It uses BUS-MAN and BUS-CMP to get the job done.

    BUS-CMP
    Business components are the business version of DAL-DOM, they handle complex situations and are built up of, and manage, multiple object references and relationships.

    UI-PROC
    User Interface Processes consume Business Entities and Business Components and break them down for each of your simple form controls. They also control workflow and direct behavior of your form. (Think Model-View-Controller architecture.)

    UI-Display
    Your form and simple controls behavior and logic.

    <Whew>
    Now, do you really need all that?

    That depends on the scope of your application and the impact on your business. If the impact/scope is big, then so is the program's architecture. If your just building a quick web page that someone in accounting needs, then just bind a Grid to a DataTable and move on with life. Most likely it will be incorporated in a larger project, or require an overhaul in the short term anyway.

    Being extensible is the main reason for all the layers. The fewer things that depend on you (for services, libraries, etc) and less extensible (add-on hooks, etc) the less you have to worry about layering everything. Unless you like all that extra work when the boss says, "Today I need you to add this column and delete this one."

    Half the battle is knowing when to "go all out" and when to "hack and slash". Now if only I could teach my management that... Smiley

    I hope that helps sort things out...
    Check out Pattern's and Practices for more architecture information.

  • User profile image
    JohnAskew

    I think what Shagate is saying in short form is that the data layer splits out the data for the business layer (DAL-DOM), so the business layer will just ask for relevant business data, not anything to do with schema.

    Bundling data for the business layer is what your data layer should concern itself with.

    The business layer could then request data more relevant to the UI's needs, where you get an object-graph instead of resultset rows.

  • User profile image
    ferguslogic

    Thanks a lot guys.  Great information.

  • User profile image
    mbmattox

    Hi,

    I don't know if this is of any help to you, but I ran across a similar situation where I wanted to cut down the number of round trips to the database, but I did not want to use datasets in order to do so.

    What I did is create my business entity that incorporated all the the child entities that I wanted as part of the object, and then designed my stored procedure to do what you would typically do in loading a dataset(multiple datatables in the same sproc call), but instead of returning a dataset to be used, I returned this as a SqlDataReader object.  Then as I loaded my business object, I could make calls to the Reader's .NextResult method to then load child entities for the root entity.

    This let me use my defined behavior for the business object and eliminate all the chatty calls to load the object.

    The only caveat I found to this was that in some of the RDBMS's out there, whether or not they allow for returning multiple resultsets in a sproc is questionable.  Since you mentioned a totally native Microsoft environment, though, I thought I would throw this out there.

    Regards,

    Mike Mattox

  • User profile image
    ubercoder

    mbmattox wrote:
    Hi,

    What I did is create my business entity that incorporated all the the child entities that I wanted as part of the object, and then designed my stored procedure to do what you would typically do in loading a dataset(multiple datatables in the same sproc call), but instead of returning a dataset to be used, I returned this as a SqlDataReader object.  Then as I loaded my business object, I could make calls to the Reader's .NextResult method to then load child entities for the root entity.



    This is exactly my approach.
    You can load any business entity like this.

    The problem that rises here is what kind of custom entity???

    If you need Customer, Order, Order Lines what you do???
    You fill 3 business entities or create one that contains all the above as nested ones?

    I personally do the first. This way I keep my business entities directly mapped to my database tables.
    This is the only way if you are using code generator for the business entities like I do.





  • User profile image
    zhuo

    Shagate wrote:
    

    Think about "responsibility", not functionality.
    (I still struggle with this myself sometimes.)

    There is no such thing as a "perfect" model for all applications. The folks a Pattern's and Practices give some great examples of how to write fully extensible applications.

    Everyone has a different idea of how to approach problems, but here's my "big" solution. Depending on the scope and duration of the project I would certainly combine layers for maintenance, etc.

    PS: Change the DAL to something more generic if you're not SQL specific.

    Example:

    SQL-DB:    Inventory, Customer, Order Tables
    ----
    DAL-CMD: Static SqlCommand Objects
    DAL-MAP:  SqlCommand / DataEntity Mapping
    DAL-ENT:  Data Entities
    DAL-MAN: Data Manager
    DAL-DOM: Data Domains
    ----
    BUS-ENT: Business Entities
    BUS-MAN: Business Manager
    BUS-WF: Business Workflow
    BUS-CMP: Business Components
    ---
    UI-PROC: UI Processes
    UI-DISP: UI Display

    Okay, now for the long winded explaination...

    DAL-CMD
    These are SqlCommand objects pointing to stored procedures in your database. These could also be simple SQL calls if you want. They are static, so any data object can grab one at any time. I wrote a simple code generator to create this class from my database.

    DAL-MAP
    This class contains functions that map one-to-one to the static SqlCommand objects in DAL-CMD. The methods consume a Data Entity class (Orders for example) and map the properties to the SqlCommand object. You could also create methods that consume parameters. Again, I use a code generator to create this class directly from the database tables.

    DAL-ENT
    This class contains Data Entities which are created based on your Database Tables. They have static members which reference the DAL-MAP CRUD operations through interfaces.  This way the DAL-MAN can execute generic commands. Again, these classes are auto-generated from a script.

    DAL-MAN
    The Data Manager is responsible for executing CRUD commands and Transaction support. Think of it as a connection gatekeeper. It will invoke the DAL-ENT's Update, Delete, Insert, etc. interface methods. Need Transactions? Pass an array of DAL-ENT's to it.

    DAL-DOM
    This is the part you're interested in. This class (or these classes) manage result sets, and break them down into their Data Entities. So in your case, define a class (CustomerInventory) with the constructor you need (InventoryId, CustomerId, OrderId) that will ask for a DataSet with 3 tables returned from a single batch command (only 1 SQL call), and define a set of TableAdapters to map to each table. It will then pass the DataRows to the DAL-ENT class to create instances of it in an array/collection and hold a reference. Your BUS-MAN class can then create BUS-ENT from the DAL-DOM class you created.

    BUS-ENT
    Business Entities contain the same basic information as a Data Entity, but with additional data more relevant to your application's needs (not the database's). Also, I believe Pattern's and Practices suggests using a separate Translation class to map Data Entities and Business Entities together. It's your call...

    BUS-MAN
    The Business Manager(s) and Data Manager talk together to form a solution. The respective Entity classes are there to act as containers for information and basic functionality. I would use a 'manager' to handle envoking a data operation and the translation of a Business Entity into a Data Entity and visa-versa. Similar object types should be grouped together under a single manager.

    BUS-WF
    These classes manage the workflow processes (and possibly state) for your application. It uses BUS-MAN and BUS-CMP to get the job done.

    BUS-CMP
    Business components are the business version of DAL-DOM, they handle complex situations and are built up of, and manage, multiple object references and relationships.

    UI-PROC
    User Interface Processes consume Business Entities and Business Components and break them down for each of your simple form controls. They also control workflow and direct behavior of your form. (Think Model-View-Controller architecture.)

    UI-Display
    Your form and simple controls behavior and logic.

    <Whew>
    Now, do you really need all that?

    That depends on the scope of your application and the impact on your business. If the impact/scope is big, then so is the program's architecture. If your just building a quick web page that someone in accounting needs, then just bind a Grid to a DataTable and move on with life. Most likely it will be incorporated in a larger project, or require an overhaul in the short term anyway.

    Being extensible is the main reason for all the layers. The fewer things that depend on you (for services, libraries, etc) and less extensible (add-on hooks, etc) the less you have to worry about layering everything. Unless you like all that extra work when the boss says, "Today I need you to add this column and delete this one."

    Half the battle is knowing when to "go all out" and when to "hack and slash". Now if only I could teach my management that... Smiley

    I hope that helps sort things out...
    Check out Pattern's and Practices for more architecture information.




    Lately, i've been questioning the validity of going to the extent that was described above in layering an application. What do you hope to gain by layering to the extent that you have done above?

    I can see that the resulting code would be quite pretty, that is if business requirement don't change, but the likelihood is that business requirements will change, and due to the high level of dependency between each layer, any slight change in DB schema means a change in every other layer along the way to the UI.

    As far as I can see all these layering have been created in the name of abstraction and strict OO. But I want ask why do we need to abstract the DB Schema from UI code, what do we hope to achieve by doing this? Further to my point, if the DB schema changes then what's the likelihood that the UI is not going to change? My guess is that 90% of the time the UI is going to change with the DB schema change, if this is the case why put in this extra data abstraction?

    Maybe I don't appreciate all of these because I haven't worked on application that have tens or hundreds of developers working on them, but I can't see how such a layering scheme would benefit a project of such scale. I would imagine a project of such scale would need to be managed through "divide and conquer". And for the case of smaller projects, as you've mentioned you definitely don't need it.

    This would make a real good discussion.

  • User profile image
    ubercoder

    zhuo,

    Another reason to break an app in different ddls appears on the need of a distributed application to run in multiple servers.

    This way you can have your business layer run in a set of servers while data layer runs in another set.

  • User profile image
    zhuo

    ubercoder wrote:
    zhuo,

    Another reason to break an app in different ddls appears on the need of a distributed application to run in multiple servers.

    This way you can have your business layer run in a set of servers while data layer runs in another set.


    This is the first time i've heard of such thing. I understand why you would want to break things up by function, e.g. HR, Accounts etc, but I've never heard of breaking things up by layers. What's the point?

  • User profile image
    ubercoder

    I'm talking about the backend not the client.

    In a distibuted application scenario some application functionality may require to be distributed into multiple application servers.

    In cases like these, breaking  specific functionality into it's own dll will allow you to host it to it's own servers.

    Breaking things on the client may help in auto-update scenarios where you only need to update a dll not the whole application.

  • User profile image
    figuerres

    Just a quick thought --  I am getting ready to do an app that will have some tables to load -- a bit like your problem.


    one idea I have is to look at how often things change and how big they are.


    for example a list of staff names, in a small business (less then 20 staff)

    it might be "just as well" to load the list 1 time and keep a local in-memory list for the run of the app.

    so when the order is loaded the name of the staff is in-memory already. no need to hit the staff table.

    there are several places that can happen.

    in other places I may use a list with an LRU algoritiim

    like say "products"  load 5-10 and when the new order / or loading an order needs data and hits my list my list can add them from the db and keep them in memory -- at some point I drop items from the list based on the date/time they have been accessed.

    also I am thinking of using the same logic that asp.net has
    asp.net as a sql-cache-dependancey thing...

    if i can "borrow" some of that code it would be usefull.

    in that it casues the db to notify the web server when the items it has are invalidated by a db change.

    so make that my clients cached records -- the db rasing an event to tell my code to fetch new data.

    do that on a background thread and..... that could be huge if I can do it right!

    load the staff table 1 time and when another pc edits a record the other pc's get a new copy -- the user never sees the db work in the background.

    just a few ideas ...

  • User profile image
    zhuo

    ubercoder wrote:
    

    I'm talking about the backend not the client.

    In a distibuted application scenario some application functionality may require to be distributed into multiple application servers.

    In cases like these, breaking  specific functionality into it's own dll will allow you to host it to it's own servers.

    Breaking things on the client may help in auto-update scenarios where you only need to update a dll not the whole application.



    Just to make sure we are on the same page, you are talking about distributed application where you have distributed services running as the backend and end-user interface running as the client on multiple machines. And you are saying that for whatever reason we want to split the backend service into several services running on different servers.

    Even in this context, It doesn't make any sense to me to use layering as an approach to split the app. We are talking about a 3-tier application not an SOA approach or a distributed application hosting its service on multiple machine.

    But I think we are getting a little off track here. Maybe we could move back to our discussion on the advantage of layering the application as discussed. and I would really love it if someone could discuss some of the things i've raise in my first post for this thread.

  • User profile image
    ubercoder

    zhuo,

    I didn't know you are talking about a "fat" client.

    About the original post, I believe the anwsers from the other niners, including myself, should be satisfactory.

     

     

  • User profile image
    JohnAskew

    zhuo wrote:
    
    ubercoder wrote: zhuo,

    Another reason to break an app in different ddls appears on the need of a distributed application to run in multiple servers.

    This way you can have your business layer run in a set of servers while data layer runs in another set.


    This is the first time i've heard of such thing. I understand why you would want to break things up by function, e.g. HR, Accounts etc, but I've never heard of breaking things up by layers. What's the point?



    Being able to replace a layer without having to recompile the host.
    Deployment!

    You can have a data layer for Oracle, another for MySql, ad infinitum. Why cram all that code in one file and lookup which to use at run-time?

    Take a look at the Composite UI Application Block as implemented through the Smart Client Software Factory. This allows re-deployment of seperated parts; UI from process, layers from host... it is a very mature architecture (but not for every small application).

  • User profile image
    ubercoder

    ubercoder wrote:
    

    Breaking things on the client may help in auto-update scenarios where you only need to update a dll not the whole application.



    As I already mentioned.

  • User profile image
    JohnAskew

    ubercoder wrote:
    
    ubercoder wrote: 

    Breaking things on the client may help in auto-update scenarios where you only need to update a dll not the whole application.



    As I already mentioned.


    Well stated. I am a bit surprised with the question, actually...
    Wink

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.