Layers Pattern in Practice - CodeProject
Layers Pattern in Practice - CodeProject
Page 1
Download executable - 916 KB Download setup executable - 1.25 MB Download sources - 0.99 MB Download additional libraries - 514 KB
Contents
Introduction Background Technology used Conceptual integrity Objectives Data Access Layer Entity Objects Business Logic layer Presentation Layer Datagrid Disconnected Data Environment
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 2
Introduction
The main task of this article is to present a generic application's lifecycle, with problems and solutions to common issues which programmers face on a daily basis. Once you start building an application, you ought to take into consideration lots of things, mainly from different theoretical areas of programming. Of course, as an architect of a software product, you might bump into different problems, mainly related to understanding of the entire complexity of the project, at the very beginning of the specification design. The aim of the text written below is to highlight key points which one needs to take into account at the start of the application conception. Herein, a simple WPF software product will be presented as an example, starting from its specification design, going through the development of a 3-tiered class hierarchy (User interface design - GUI; Business Logic - further noted as BL; and Data Access Layer - DAL), and ending up with a setup project and post-release debugging. One can argue that "programming is an art" and there is no need to define generic rules beforehand, constraining the architect's mind. Indeed it is so, and the article will actually try to show solutions to common problems in Windows application design, leaving the choice of the lifecycle methodology to the architect.
Background
"Where architecture tells what happens, implementer tells how it is made to happen" Blaauw The tutorial written below will try to encompass both the architect's and the implementer's mind by showing what and how problems are solved together. The software example which is going to be presented is a BillsManager. The application's ultimate goal is to allow a client to manage his bills (simple and straightforward).
Technology used
I decided to build BillsManager as a Windows application based upon the rising in popularity Windows Presentation Foundation technology. I find it very prominent and really a step forward in Windows based development. The Data Access Layer will be constructed around XML files which will store the user's bills. So, as it can be inferred, the minimal requirement is .NET 3.5 SP.1.
Conceptual integrity
"Conceptual integrity is the most important consideration in system design" Frederick Brooks Indeed, as practice shows, in order to successfully build any software, one should clearly define written specifications (a necessary tool, although not a sufficient one). From theory to practice - below is a list of the main objectives which BillsManager is going to meet.
Objectives
Show how much money a person needs to spend this month Archive the payments Show the nearest deadline Chart the expenditures Chart the expenditures by an interval of time Show information about every bill Give the possibility to export the archive in a file for backup Assume that the target audience is not English-speaking BillsManager will try to accomplish these fairly straightforward objectives. Although one can argue that these specifications are not enough to build a successful application, I will leave them as they are in order, not to increase the complexity of the example on which all the explanatory part is built upon.
3-tier application
"Software architecture encompasses the set of significant decisions about the organization of a software system, including the selection of the structural elements and their interfaces by which the system is composed; behavior as specified in the collaboration among those elements; composition of these structural and behavioral elements into larger subsystems; and an architectural style that guides this organization. Software architecture also involves functionality, usability, resilience, performance, reuse, comprehensibility, economic and technology constraints, tradeoffs, and aesthetic concerns" P.Kruchten, G.
Booch, K. Bittner, R.Reitman
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 3
As it is stated in the above self-descriptive paragraph, while designing the application, the software architecture team should have in mind a strong view about how the requirements will be projected into the real environment, and how this environment can be as generic as it is possible. Separation of interfaces, business logic, and data access layer is a very important and also common need in software development. Essentially, nowadays, reusability plays a crucial role within the Object Oriented paradigm. The BillsManager application will implement the 3-layered structure using the Layers Pattern guidelines. Figure 1 below shows a general view of the systematization which is going to be used.
A 3-layered pattern is used in order to build better organized software parts. It provides a mechanism of defining reusable business components, alongside with deployment flexibility and smart resource connection management. All the components which are responsible for data visualization (e.g. DataGrid) will be placed in the Presentation Layer. All business logic rules will be encapsulated in business components within the BL layer. Finally, all data related code needs to be defined within the Data Access Layer. Frequently, the DAL layer is responsible for database access. In the example provided in this article, the database will be an XML file (but that does not make a difference as long as the GUI and the BL are not aware of the underlying data source). A more advanced explanation of this pattern can be found in Enterprise Solution Patterns using Microsoft .NET.
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 4
As it can be seen from the methods which need to be implemented, they define a very general contract which can be summarized in the following responsibilities: Read bills from the data source (can be any data source: XML, Excel, SQL Server, network resource, etc). Delete bills from the data source. Update bills in the data source. Insert bills in the data source. Get or set the settings for the data source (e.g., connection string, path to archive file, etc.). It is important to point out that the contract should relinquish the following rule within [IN] and [OUT] parameters. It should have: Most general parameters at the input [IN] (E.g., IEnumerable<guid> interface) Most specific return types [OUT] (E.g., Bills) Subsequent to the interface definition, it is valuable to mention that the Business Logic programmer will access methods of the DAL Manager only through an interface instance.
private IDalBillsManager _dalManager = XmlDalBillsManager.GetInstance();
If in a future development cycle, you choose to switch the DAL Manager to another version or to an entirely new library, you will need to change only one line of code, which is the assignment operation written above. After doing this, the entire business logic library will work as it is supposed to, without requiring any other changes in the code. The responsibilities, defined in the IDalBillsManager interface will be held by the Data Access Manager. In our case, it will be an XML-based DAL Manager, meaning that all the bills information will be stored in an XML archive file. Below, you can see the XML schema on which our archive will relinquish (this schema is located in BillSchema.xsd under the BillEntityLib project folder). It is very important to note that our XML will use strongly established rules of composition.
<?xml version="1.0" encoding="utf-8"?> <xs:schema id="billschema" targetNamespace="http://tempuri.org/billschema.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/billschema.xsd" xmlns:mstns="http://tempuri.org/billschema.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Bills"> <xs:complexType> <xs:choice minOccurs="1" maxOccurs="unbounded"> <xs:element name="Bill" type="Bill"/> </xs:choice> </xs:complexType> </xs:element> <xs:complexType name="Bill"> <xs:sequence> <xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1"/> <xs:element name="DueDate" type="xs:dateTime" minOccurs="1" maxOccurs="1"/> <xs:element name="Amount" type="xs:decimal" minOccurs="1" maxOccurs="1"/> <xs:element name="AddedOn" type="xs:dateTime" minOccurs="1" maxOccurs="1"/> <xs:element name="Status" type="xs:string" minOccurs="1" maxOccurs="1"/> </xs:sequence> <xs:attribute name="ID" form="unqualified" type="xs:string"/> </xs:complexType> </xs:schema>
The XSD file describes the content that is allowed in an XML document in order for the last one to be considered valid (i.e., within the defined constraints). If you are not comfortable with XML schemas and their rules, please consult any available material on this topic. Our XML schema is formed from the following elements:
Bills (Root element) Bill (Will represent every bill) Name (Name of the bill) DueDate (Bill's due date) Amount (Bill's amount) AddedOn (When was the bill added on) Status (Bill's status)
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 5
Our XML-based Data Access Manager will be a singleton object (meaning that where will be only one instance of the object in the entire application lifecycle). If you are not familiar with singletons, please consult the Singleton pattern article for a better explanation. The following figure presents the class diagram for the XmlDalBillsManager class. (Please notice that it implements the IDalBillsManager interface.)
Consequently, our XML based manager should support thread-safe operations (so that the archive does not get corrupt if multiple threads attempt to write to the database at the same time - which could result in data loss or unpredictable behavior). Thus, the user is allowed to call the operations Insert, Read , Update, and Delete without a clue of how the archive is protected from corruption. As it is not difficult to observe, here we can implement the Readers/Writers thread algorithm in order to solve the above mentioned problem. Herein, the manager will implement the following policy: If somebody reads the data from the archive, other readers are allowed to perform the same operation (access for writers is prohibited). If somebody writes the data into archive, no other thread (read or write) can perform any operation with the above mentioned data source. For a more detailed review, please consult any available material related to the Readers/Writers threading algorithm.
Entity Objects
Having the XML schema defined, we can now generate entity classes. These are the classes which do not hold any responsibility (do not have any methods). They are just binary representations of the data from the XML. The Bills and Bill classes will perform only the storage function within the Business Logic Domain (they will not have any additional methods except getters/setters for their properties). There are different approaches within the Layers pattern regardless of the entity and business logic elements. You can define the business logic methods (Insert, Read , Delete, Update) in the same classes in the entity library (we'll see later what methods will actually be implemented in the business domain). The others can be separated in two different classes. I've used the following separation of business components: Entity classes Manager class Don't worry if you do not understand the entire concept; you'll get it once you take a look at the actual implementation. I've decided to perform the separation, because it is more natural to have exactly the same copy of a Bill item in the computer memory as it is in the data source (no matter what data source: XML, text, Excel, relational database, etc.). At the same time, these entity objects will actually be serializable. This feature will allow any .NET programmer to extend the program functionality by adding a new layer within the Business Logic and the Data Layer and pass objects from the Business Domain to the Data Domain in serialized state. Serialization allows to save an object's state (Bills and Bill ) into any stream (memory, network, file, etc.) , and pass it within the Application Domain or Remoting services. From BillSchema.xsd , we can see that there will be only two entity classes: Bills and Bill . .NET provides an easy mechanism for generating .cs files from .xsd schemas. This mechanism is achieved through the XML Schema Definition tool. One can access xsd.exe through Visual Studio's command prompt or by opening the command line and navigating to c:\Program Files\Microsoft Visual Studio 9.0\VC>. The command which needs to be applied is xsd [name of xml schema].xsd /c. By applying this command, the XSD tool will generate partial classes which correspond to the schema definition.
//-----------------------------------------------------------------------------// <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.4926 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //-----------------------------------------------------------------------------using System.Xml.Serialization; // // This source code was auto-generated by xsd, Version=2.0.50727.3038. //
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 6
/// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.303")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/billschema.xsd")] [System.Xml.Serialization.XmlRootAttribute( Namespace="http://tempuri.org/billschema.xsd", IsNullable=false)] public partial class Bills { /*Bills collection from the datasource*/ private Bill[] itemsField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute("Bill")] public Bill[] Items { get { return this.itemsField; } set { this.itemsField = value; } } } /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute( Namespace="http://tempuri.org/billschema.xsd")] public partial class Bill { /*Each bill from the datasource, has exactly the same properties*/ private string nameField; private System.DateTime dueDateField; private decimal amountField; private System.DateTime addedOnField; private string statusField; private string idField; /// <remarks/> public string Name { get { return this.nameField; } set { this.nameField = value; } } /// <remarks/> public System.DateTime DueDate { get { return this.dueDateField; } set { this.dueDateField = value; } } /// <remarks/> public decimal Amount { get { return this.amountField; } set { this.amountField = value; } } /// <remarks/> public System.DateTime AddedOn { get { return this.addedOnField; } set { this.addedOnField = value; }
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 7
Following is the class diagram for the Entity objects. Please note that I've added some additional class members to those which were generated. (I will explain later why there was a need to implement the INotifyPropertyChanged interface by the Bill class).
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 8
/// <summary> /// Settings /// </summary> object[] Settings { get; set; } }
You can see that the aforementioned interface defines exactly the same methods as the Data Layer interface IDalBillsManager. Yes, indeed the methods are the same because the actual responsibilities are also the same. The only difference is that every manager (either from the Business Domain or from the Data Layer) is responsible for different logical operations on the data which passes through. As an example, see the following figure:
As can be seen, even though the method signature for both the Business and Data Domain is the same, internally, they are responsible for different logical interactions within the objects. In the above example, the Business Domain insertion operation is responsible for: Validation Sending the request to the Data Layer Checking whether the Insert operation occurred or not Throwing an exception if appropriate At the same time, the Data Layer is responsible for: The actual insert operation into the data source One can argue that there was no need to define two different interfaces with the same signature for the Business and Data Domains; instead, I should have used the same interface for both. I definitely consider this argument a mistake, because the client programmer would be able to cast Business and
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 9
Data Logic objects into the same interface, without a clue of whether he accesses methods from one or the other domain. This approach breaks the entire philosophy of layer separation and should never be used.
Presentation Layer
The last but not the least layer in the pattern is the Presentation Layer. It is actually responsible for the interaction between the user and the machine. There are plenty of topics which one might find useful while developing a GUI (Graphical User Interface). If you are building a WPF application, you might find it useful to check Sacha Barber's articles here at CodeProject. Anyway, I will cover several of them which I think are the most generic ones. We'll start with the DataGrid, a component which is responsible for presenting the data to the end user. Then we'll move to a more specific problem as charting the data and localizing string resources.
Datagrid
By default, .NET 3.5 does not ship a WPF datagrid component. There are different viewpoints whether it is good or not. I will just point out that I decided (like many other developers) to use the WPFToolkit which can be freely downloaded from the CodePlex site (click here), in order to have a built-in DataGrid with all the bells and whistles. Personally, I find those components really useful, so I really encourage everybody to explore them. A core action one needs to define in a data grid is its binding to a data source. Binding is the concept of establishing a connection between a UI (user interface) component and the business logic. There is a very nice article on the MSDN site which explains how binding works in WPF components (click here). Typically, each binding has these four components: a binding target object a target property a binding source a path to the value in the binding source to use For example, if you want to bind the content of a TextBox to the Name property of an Employee object, your target object is the TextBox, the target property is the Text property, the value to use is Name , and the source object is the Employee object. Here is the XAML code which declares and defines the DataGrid for BillsManager with the necessary binding.
<WPFToolkit:DataGrid x:Uid="_dgBills" Style="{StaticResource DataGrid}" ItemContainerStyle="{StaticResource ItemContStyle}" x:Name="_dgBills" Margin="8,42.96,8,167.08" IsReadOnly="False" AutoGenerateColumns="False" CanUserAddRows="False"> <WPFToolkit:DataGrid.Columns> <WPFToolkit:DataGridTextColumn Header="Bill" Width="100" Binding="{Binding Name}"/> <WPFToolkit:DataGridTextColumn Header="Due Date" Width="100" Binding="{Binding DueDate, Converter={StaticResource DateConverter}}"/> <WPFToolkit:DataGridTextColumn Header="Amount" Width="75" Binding="{Binding Amount}"/> <WPFToolkit:DataGridTextColumn Header="Added On" Width="100" Binding="{Binding AddedOn, Converter={StaticResource DateConverter}}"/> <WPFToolkit:DataGridComboBoxColumn Header="Status" Width="75" SelectedItemBinding="{Binding BillStatus}" ItemsSource="{Binding Source={StaticResource myEnum}}"/> </WPFToolkit:DataGrid.Columns> </WPFToolkit:DataGrid>
As can be seen, each DataGrid column is bind to a specific property from our Entity's library Bill class. Name - Binding="{Binding Name}" DueDate - Binding DueDate, Converter={StaticResource DateConverter} Amount - Binding="{Binding Amount}" AddedOn - Binding="{Binding AddedOn, Converter={StaticResource DateConverter}}" Status - SelectedItemBinding="{Binding BillStatus}" ItemsSource="{Binding Source={StaticResource myEnum}}" It is worth mentioning that the Converter item from the DataGrid column definition will perform the formatting operation of DataTime values (it will show the date in short format DD.MM.YY). Shown below is the actual code for the converter item:
[ValueConversion(typeof(DateTime), typeof(String))] public class DateConverter : IValueConverter { /* * Convert each data item from the data grid into short format DD/MM/YY */ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 10
The code above binds the data to Bills, following this logic: "To bind the datagrid to data, set the ItemsSource property to an IEnumerable implementation. Each row in the datagrid is bound to an object in the data source, and each column in the datagrid is bound to a property of the data object. In order for the DataGrid user interface to update automatically when items are added to or removed from the source data, the DataGrid must be bound to a collection that implements INotifyCollectionChanged , such as an ObservableCollection<T>. In order to automatically reflect property changes, the objects in the source collection must implement the INotifyPropertyChanged interface". Click here for more details.
Bills from the data source are represented in the computer memory via the Bills class. Afterwards, these items are embedded in the ObservableCollection<Bill> collection. In this way, we can add, remove, edit elements of underlying list of bills without actually taking care of
updating the user interface to reflect the changes (.NET will perform this automatically). Click here for more details. As written above, we'll bind the data grid item source property to ObservableCollection<Bill>.
_collection = new ObservableCollection<Bill>(bills.Items); //define the ObservableCollection<T> class this._datagrid.ItemsSource = _collection; //bind the data to the datasource
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 11
The DataRowBackgroundConverter class's Convert method will be invoked. According to the data which is set in the data row, it will return Yellow or no color.
[ValueConversion(typeof(object), typeof(int))] public class DataRowBackgroundConverter : IMultiValueConverter { #region IMultiValueConverter Members /* * Check each element for the corresponding background color */ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values[0] is DateTime) { DateTime dueDate = (DateTime)values[0]; string status = values[1] as string; if (dueDate < DateTime.Now && BillHelper.Convert(status) == BillStatus.Unpaid) { return new SolidColorBrush(Colors.Yellow); /*Return Yellow if DueDate is overdue and BillStatus is unpaid*/ } } return null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #endregion }
As you can see, WPF offers great flexibility in terms of data binding and data formatting.
Chart
Charting is a topic which is definitely very often discussed on programming forums. Many people find it challenging, because lots of things need to be taken into consideration. I decided to use a third party charting tool rather than the one defined in the WPFToolkit. It can be freely downloaded from the Visifire site. It is a framework which provides Open Source data visualization components. I find it very nice and easy to use. The things which I would like to point out are, that the chart itself draws the data according to a set of different parameters, which can be set in many ways (DataSeries - series of data points which actually represent the pair X, Y, where F(x) = Y; Axis - pie, line, bar, 2D, 3D, etc., Title - the actual title text). In order to have an extensible mechanism of drawing the chart using different Axis , I decided to use the Strategy pattern. The strategy is actually the way how axis will be represented (this is done in order to have three different types of chart: Line, Bar, Pie). The following figure presents the class diagram for these items:
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 12
Consequently, chart properties are set according to one of the methods of drawing it:
//chartType object of type ICharting public static void SetChartValuesAsync(Chart chart, IEnumerable<bill> list, ICharting chartType, IntervalTypes interval, ChartValueTypes chartValueType, bool scrollingEnabled, bool erasePrevious) { if (chart != null) { chart.Dispatcher.BeginInvoke(new Action(delegate() { if (erasePrevious) { chart.AxesX.Clear(); chart.AxesY.Clear(); chart.Titles.Clear(); chart.Series.Clear(); } // Add title to Titles collection chart.Titles.Add(chartType.GetTitle(list.First().Name)); /*Set Axis via interface instance*/ chart.AxesX.Add(chartType.GetAxis(interval, list)); chart.ScrollingEnabled = scrollingEnabled; chart.Series.Add(chartType.GetDataSeries(list, chartValueType)); }), null); } }
As we can see, the mechanism is maintainable and extensible, because even if we add new types of charts in the following versions of the application, we only need to implement the corresponding ICharting interface and pass an object of its type to the SetChartValuesAsync method. In the figure below, we can see an example of the aforementioned settings adjustment:
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 13
Localization
Localization is the process of preparing an application to run in multiple locations. The .NET Framework provides very thorough support for localizing all types of client applications. Culture information and manipulation in the .NET Framework is exposed through an instance of the CultureInfo class, which can be used to read culture settings for a given locale, as well as setting a specific locale in an application. Every thread maintains a set of culture information stored as CultureInfo settings in the CurrentCulture and CurrentUICulture properties. WPF applications have two possibilities to set different locale statuses: LocBaml tool Resx approach In my application, I'll be using the Resx resource files approach (mainly because it is a classical way of setting different locales in Windows Applications). Resx files are XML based files which are compiled into resource.dll libraries and can be loaded into the application via the ResourceManager class. The following snippet shows the code which performs the actual load:
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; string culture = ConfigurationManager.AppSettings["Culture"]; if (!String.IsNullOrEmpty(culture)) if(culture != "Default") currentCulture = new CultureInfo(culture); ResourceManager resx = new ResourceManager( "BillPayManager.Properties.Resources", typeof(App).Assembly);
The currentCulture object will hold a thread's culture description. The resx object will allow us to query the .dll file with the actual cultural specific information. The default culture is read by the BillsManager application from the application configuration file (Key = "Culture") . It is important to mention that if the App.config file doesn't explicitly set culture information, then the en-US culture will be used. Here is a query example:
this._lTotalAmount.Content = resx.GetString("TotalAmount", currentCulture); this._lNearestDeadline.Content=resx.GetString("NearestDeadline",currentCulture);
The resource files are stored within the folders with the same name as cultural info (en-US, ro-RO ). Next, we can visualize how MainAssembly gets the resources from the corresponding directories (see figure).
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 14
In order to change the culture, please change the BillPayManager.exe.config file or change it via the Miscellaneous tab:
<appSettings> <add key="Culture" value="ro-RO"/> </appSettings>
Be attentive while changing the Culture key. You can explicitly set only cultures defined in the corresponding sub folders (en-US or ro-RO).
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 15
The ProbeFilePermissions() method probes the location for FileIOPermissionAccess.Write, and if the user lacks the specified permissions, returns an alternative path to which the user has Write privileges.
Testing
Testing is one of the most unpleasant parts of developing an application. There are different approaches to the way how testing is performed (Manual, Automation testing, Unit tests). They depend mostly upon what methodology was used in software development. I prefer using Unit tests because there is a great way of visualizing the Data Flow, Exceptions, Assertions, etc. In order to write a unit test, we needs to install the NUnit framework, freely downloadable from their official website. Once installed, you can use features available in the NUnit.Framework namespace in order to test any public method. For more information about writing Unit Tests, click here.
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 16
Source code
I've written the application using Visual Studio 2008 TS. The source code is structured as follows: BillManagerUninstallAction project. Custom uninstall action implemented in order to delete all the associated files (archive and trace output) once the application is uninstalled from the user's machine. BillPayManager project. This is the actual WPF application. The GUI was developed using the Expression Blend 3 IDE. BillsBusinessLogicLib project. The library contains all the necessary classes for Business Layer management (BL). Main class - BillsManager. BillsDalLib project. The library where all XML data access related code is located. The main class for DAL is - XmlDalBillsManager. BillsEntityLib project. Entity library. TestBillPayManager project. Several Unit Test fixtures are written in this project. I've deleted the project from the solution configuration in order for people who don't have NUnit installed on their machines to be able to compile the solution without errors. If you want to build it, please add it to the project. TestBillPayManagerManual project. Manual tests written for GUI testing. The project is also unloaded. SetupBillPayManager project. Setup for BillsManager. In order to successfully compile the project, we need additional libraries referenced in the BillPayManagement project. These libraries are: AvalonControlsLibrary WPFToolkit Visifire
Conclusion
During my student's life, I've always had enough books on software design. Even if each of them provides very nice analysis of software architecture, none of them speaks about issues related to practical implementation. This is due to the fact that authors are most likely to speak in abstract terms, in order not to tie themselves to a specific technology (.NET Framework in my case). This is of course great, but I've always wanted a guide which will have as an example a complete product, starting from the specification design and ending up with the setup project. The aim of this article is to speak about the main issues related to .NET Windows application design, not in terms of theory, but in terms of practice. You can argue that architecture design is a topic which differs from one product to another, and it's mostly impossible to speak about every tiny problem which you will encounter during an application's lifecycle. This is of course true, but the article's target audience is "computer science students" and not senior developers who already know all the stuff written in here. That's why I've designed a simple WPF application in order to have a generic guide for students (or uninitiated WPF developers) who would like to build these types of applications. I've tried to keep the article's size as small as possible, but somehow it extended above initial expectations. Thanks for reading.
Thanks
Alex Railean - http://railean.net/ for helping me writing this article. Victoria - for being kind.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
Ciumac Sergiu
Software Developer Moldova (Republic Of) Member Interested in computer science, math, research, and everything that relates to innovation. Big fan of C# programming language, but don't mind developing under any platform/framework if it explores interesting topics. In search of a better programming paradigm.
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM
Page 17
http://www.codeproject.com/Articles/75082/Layers-Pattern-in-Practice?display=Print
2/5/2013 2:09:53 PM