Home > Articles > Web Development > ASP.NET

Silverlight Best Practices: Modular Code

Jeremy Likness (Microsoft MVP for Silverlight) covers the benefits of building modular Silverlight applications. Learn what patterns help facilitate highly modular code, and how to leverage tools like the Model-View-View Model pattern (MVVM) and the Managed Extensibility Framework (MEF) to build dynamic, testable, maintainable, and extendable line-of-business applications.
Like this article? We recommend

In this series, you’ve learned about best practices for developing Silverlight LOB applications. LOB applications share specific qualities that make it ideal to build them in a modular fashion. They are often engineered by multiple programmers and span teams between design, development, and testing. Some applications can grow large and quite complex. LOB applications often follow aggressive release cycles to keep up with customer demand and require the ability to easily extend the application and provide customer-specific plug-ins and extensions.

All of these features can be effectively addressed using a modular design in your Silverlight code. Modular development is breaking a software application into smaller, interchangeable components. Modules are focused on specific functions and contain all of the code, data, and logic necessary to accomplish that function. Modules often interact with other modules through interfaces, events, and messages. A well-designed module should have minimal dependency on other modules in the system.

Silverlight is ideal for modular code because it contains all of the features necessary to produce a flexible, extensible, and modular system. Namespaces provide scope and context for modules. Data-binding coupled with the MVVM pattern allows for separation of presentation logic from the rest of the application to facilitate a parallel workflow between designers and developers. The Managed Extensibility Framework (MEF) is part of the runtime that supplies services for inversion of control, discovery, lifetime management, and extensibility. Through MEF, you can also facilitate runtime extensibility by exposing a plug-in model and dynamically loading new modules into the application.

Before you can take advantage of any of these features, it’s important that your code is architected to support them. The first and most basic principle for writing modular code is to follow principles and guidelines for keeping your code decoupled. The S.O.L.I.D. design patterns help you manage this within the code base.

Follow SOLID Software Design Patterns

The SOLID principles were introduced by Robert C. Martin. The principles cover guidelines for object-oriented design. These guidelines help you determine how to separate your code between classes, namespaces, and modules within the system. The following sections give a brief introduction to the principles and what they mean.

Single Responsibility

The single responsibility states that a class should only focus on one thing. For example, an application that loads and parses a CSV file should not contain one class with the responsibility for loading the file and parsing it. Instead, one class should have the responsibility of loading the file, while another class has the responsibility of parsing a CSV stream into its components. Following this principle encourages parallel development because multiple developers typically won’t end up in the same class at the same time. It also eases the burden of testing because there is only the primary responsibility to test for.

Open/Closed Principle

A good class is open for extension. If you need to extend the behavior, you should be able to do so by creating a derived class rather than changing the class itself. The class should be closed to modification so that the only reason to modify it internally is to fix a defect. This allows third-parties to extend the functionality of a module without breaking the module or changing it in a way that might impact other modules that depend on it.

Liskov Substitution Principle

When you do extend a class, the derived class should be substitutable for the base class. In other words, you should be able to cast a derived class to the base class and have it perform exactly as expected. For example, you may be tempted to create a Rectangle class with a length and width and derive from the Rectangle to create a Square that forces the length and width to be the same. This would violate the principle because when you cast the Square to the Rectangle, it will not behave correctly because the rectangle should allow a different width and height. To honor the principle, create a base Quadrilateral class where the length and width are read-only. The Rectangle can derive from this and allow the user to set the length and width, while the Square could override the same class and provide a simple method to set the length of a side. In both cases, the base Quadrilateral class would now perform as expected without compromising the functionality of the derived class.

Interface Segregation

Interfaces should be client-specific. A great example of this from the framework is the implementation of the IComparable and IEquatable interfaces. Most value types (integers, dates, decimals, etc.) are both comparable (they can be sorted into a list) and honor equivalency (you can determine when one value is equivalent to another). As a client dealing with a generic value type, you will likely be interested in only one function or the other. Instead of providing a general interface that specifies methods for comparison and equality, the framework segregates the interface for the specific functionality. If you need to perform a comparison, you can use the IComparable interface without knowledge of any other interfaces the target type might implement. This allows for a simpler design and reduces dependencies between clients that are consuming services on a type.

Dependency Injection/Inversion of Control

The dependency injection principle reinforces the single responsibility of a class. A class that requires logging may hold a reference as an interface to the logger. If the class creates an instance of the logger, it has additional responsibility for creating the logger and a direct dependency on that implementation. There is no longer any flexibility to change what the logging mechanism is without modifying the class, which would violate the open/closed principle. Instead, the class should expose the interface but invert control by allowing some other mechanism to provide the implementation. The dependency is then injected by the framework.

To learn more about this principle, read my Informit article “Inversion of Control with the Managed Extensibility Framework.”

Together, these SOLID principles are powerful guidelines for architecting decoupled and modular software applications.

Use Namespaces Appropriately

Namespaces provide logical scope and context for classes and types. They are useful to avoid collisions between types defined across components and between third-party providers. Without namespaces, all logging mechanisms might have the same type of Logger. Namespaces allow companies to add additional context and brand the type so that you can use a Jeremy.Logger or an InformIT.Logger and avoid a collision between the type names.

Namespaces also help segregate your internal code. Companies often use namespaces to differentiate core framework and scaffolding components from product-specific ones, and to designate modules within those applications. A very common convention that follows the .NET Framework and should be specific enough to use across your enterprise is to use this namespace template:

Company.Product.Module.Folder 

For example, if I am designing a feed reader with a search module, I would place my view models in the following namespace:

JeremyLikness.FeedReader.Search.ViewModels

The key for your organization is to define a standard and stick with it so that it is consistent across the enterprise. Once you have your namespaces defined, it is easier to design your projects and assemblies. One common mistake I see (and have been guilty of myself) is to create a project for each module right away. While reducing dependencies and decoupling the application is important, there is a trade-off with complexity when you add projects. This leads to additional compile time and overhead when building the application.

If you follow a solid namespace convention, it is easy to refactor the solution into separate projects when the main project becomes too large or requires the separation. For example, you might start with a single projected called “JeremyLikness.FeedReader” with folders named “Search” and “Display” that have their own subfolders. If the project becomes too large, you can create a new project called “JeremyLikness.FeedReader.Search” and move all of the search items into that project. The namespace will be preserved and the only refactoring necessary will be to add the appropriate references and rebuild the application.

Keep it Design-Friendly with the MVVM Pattern

The MVVM pattern was covered in a previous article. One important benefit that using this pattern provides is a clean separate of design and development. The design for an application can be a lengthy process and it may change often. Traditional workflows often provide time at the beginning of the project to focus on design, and delay development until the design has been finalized. Silverlight, data-binding, and the MVVM pattern allow a parallel workflow that is far more efficient.

I worked on a large project called Looking Glass. The project called for nearly a dozen designers who produced the design over several months, moving from an initial set of wireframes to static “comps” followed by actual design assets provided in Illustrator files. The development team was also busy building the functionality of the application and the timeline required that both processes happened in parallel.

Using the MVVM pattern allowed the team to define a set of contracts up front. While the design was flexible, it was agreed that certain screens would exist and have a specific set of information. The design team focused on the presentation of the information and transition between screens while the development team built the business logic and other functionality required to obtain the information. The contract was defined by the view models that were created. Initially, “design-time” view models were built that provided sample data for the designers to work with, while the development team built the “run-time” view models that interfaced with web services and pulled in the real-time data from the backend servers.

Using MVVM allowed the design process and development process to move in parallel over the first few months. Developers used unit tests to validate their effort until the design team was finished. Once the design was complete, the XAML integration team used the design assets to build XAML based on the contracts provided by the design-time view models. The last step was simply to bind the XAML to the runtime view models created by the development team and the design ultimately merged with the development to produce a final work product. The calendar time for the project was cut almost in half by allowing the parallel design and development to take place.

Leverage the Managed Extensibility Framework

One challenge with creating modular code is discovering where implementations exist and integrating those with the main application. Fortunately, the Managed Extensibility Framework (MEF) is a built-in part of both the .NET and Silverlight frameworks that provides the services needed to pull all of the modules together. MEF provides several key services, including the following.

Extensibility

MEF enables your application to be extended through plug-ins. Specifically with Silverlight, additional modules can be downloaded at runtime and integrated with the project. MEF gives you the ability to define contracts for extension points to implement and even fire actions once the extensions are loaded.

Discovery

When following the SOLID principles listed earlier, one challenge with dependency injection and inversion of control is mapping the implementation to the control. MEF handles the inversion of control by taking responsibility for discovering implementations and making them available to classes that need them. You specify the requirement, and MEF supplies the solution if it is available within the project.

Lifetime Management

It is often important to determine the lifetime of a class when it is created for an application. For example, a short-lived helper class used by a view might be created temporarily for that view and destroyed when the view is no longer needed. A logging mechanism, however, might require a single class to be created and reused throughout the application; this is referred to as a Singleton. The Singleton pattern describes several ways to modify a class to force it to provide only a single instance, but this involves manipulating the class itself just to change its lifetime. MEF allows you to create the class ignorant of lifetime and specify how it should be shared or not shared as part of the contract. This makes it far easier to test as well as locate objects in your applications.

Metadata

The final functionality MEF provides is the ability to provide metadata about a module. This allows your application to make decisions about what modules to load and interface with. An application running in trial mode might interrogate “paid version” modules and reject those because the user has not yet upgraded to a paid account. If the system is offline, you can reject modules that require Internet connectivity. This is a very powerful feature that is used extensively in modular applications, especially ones that follow the MVVM pattern.

Load Modules on Demand

A final benefit of creating modular applications is the ability to load modules on demand. An extremely large software application might contain dozens of modules, while a particular user only has access to a smaller subset and may only use one or two in a given session. Packaging the modules in separate XAP files enables you to load the modules dynamically, as well as add new modules after the application is released to extend functionality.

There are three common module-loading scenarios:

  • The first is just for speed of the application: instead of pulling down a large package, the user can pull down a lightweight package that displays a splash screen or perhaps a slide show while downloading the other required modules.
  • The second scenario is to filter the modules available to the user based on his login and only download the modules they should have access to.
  • Finally, the plug-in model involves looking in a well-known place for additional modules to extend the application with and loading them as they become available.

You can easily integrate dynamic XAP files using the Managed Extensibility Framework. To learn more, check out my LiveLessons video, “Fundamentals of the Managed Extensibility Framework (MEF).”

Final Thoughts

Building modular applications enables parallel workflows so that you can scale the development and design teams without conflict. It is easier to fix defects, test, maintain, and extend modular applications. The combination of MVVM and MEF makes it easy to build modular Silverlight LOB applications using the well-established SOLID principles for object-oriented design.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020