Mark Gu, web architect from SunGard (Asset Finance), will be giving a presentation on how they are using ASP.NET MVC and various client-side technologies to develop their next generation Enterprise web application. The presentation will cover topics and promote discussions around pluggable websites, Knockout JS, Roslyn, code generation, and TypeScript just to name a few.
3. A global leading provider of leasing and financing
software system
Provides end-to-end capability eliminating the need for
companies to maintain multiple systems
Has been on the market for more than 10 years
More than 100 employees across multiple development
centres: New Zealand, India, and UK
Background - Business
4. Core product
.NET (Windows Forms, .NET Remoting)
SQL Server & Oracle
Crystal Report
Web portal: external facing, enables access to a small part of the
core product
ASP.NET MVC, jQuery & Knockout
Highly customizable and extensible
Background - Technology
5. Migrate the entire Windows-based application to Web-
based
No loss of functionality
Support customization and extension
Modern & responsive UI
Goals
6. Finding a balance between ease of maintenance and
per-client customization
Large code base
Complex business & UI logic
Existing employees’ skillsets
Limited resource and challenging timeline
Challenges
8. Web portal is heavily customized for each client:
logos, colours, themes
resources, labels, messages
page structure, workflows, validations
authentication and authorizations
other custom features
Clients may want to further extend their web portals
Customization for the core product is still highly desirable
Per-client Customization
9. Implements the Model-View-Controller pattern
promotes separation of concerns and code reusability
makes code easier to test
Provides an extremely extensible framework and a lot
of customization opportunities
controller factory, router handler, route constraint
view engine, Razor view build provider
value provider factory, model binder, model validation
and metadata provider
action filter, action result, …
ASP.NET MVC
10. Plug-in architecture using MEF
One plug-in site for one client, or a client’s business unit
Each site is implemented in a standalone assembly
Registration class
Controllers, models
CSS style-sheets, images, fonts
JavaScript view models
Razor views
Configuration files
Pluggable Website
11. All pluggable websites are:
deployed to a designated folder in the hosting web application
loaded and filtered at the start-up time
called into at appropriate times to perform initialization or other
business logic
metadata about site name, priority, and dependencies
routing table and constraints
resources for labels, captions, and notification messages
overridden controller or repository logic
Pluggable Website
12. Pluggable Website
public interface ISiteRegistration
{
string SiteName { get; }
void RegisterDependencies(IDependencyManager dependencyManager);
void LoadResources(ResourceGateway resourceGateway);
void RegisterIgnoreRoutes(RouteCollection routes);
void RegisterRoutes(RouteCollection routes);
}
[Export(typeof(ISiteRegistration))]
[ExportMetadata("SiteName", SiteConsts.SITE_NAME)]
public class ClientSiteRegistration : ISiteRegistration
{
public string SiteName
{
get { return SiteConsts.SITE_NAME; }
}
...
}
internal static class SiteCompositionManager
{
private static CompositionContainer _compositionContainer;
public static void Initialize()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
var fullPath = HttpContext.Current.Server.MapPath("~/Sites");
if (Directory.Exists(fullPath))
{
foreach (DirectoryCatalog dirCatalog in GetDirectoryCatalogsRecursive(fullPath))
{
AppDomain.CurrentDomain.AppendPrivatePath(dirCatalog.Path);
catalog.Catalogs.Add(dirCatalog);
}
}
var filteredCatalog = new FilteredCatalog(catalog, ...);
_compositionContainer = new CompositionContainer(filteredCatalog);
}
public static void Compose(params object[] parts)
{
if (_compositionContainer != null)
_compositionContainer.ComposeParts(parts);
}
...
public class MyControllerFactory : DefaultControllerFactory
{
[ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)]
private IEnumerable<Lazy<ISiteRegistration, ISiteRegistrationMetadata>> _sites;
public MyControllerFactory()
{
SiteCompositionManager.Compose(this);
...
public class MyRazorViewEngine : RazorViewEngine { ... }
[BuildProviderAppliesTo(BuildProviderAppliesTo.All)]
public class MyRazorBuildProvider : RazorBuildProvider { ... }
13. One pluggable website may depends on another
Pluggable Website
Core
Client A Client B
BU 1 BU 2
15. Over 10 years of investment in the current code base
More than 3.5 million LOC
More than 1000 forms and user controls
Complex business logic results in complex UI logic
multi-level cascading dropdowns
defaulting rules
cyclic fields change notifications
dynamic validations
in memory states, temporary objects
licensing, security role checking (nearly 1000 roles)
Complex Business & UI Logic
16. Some “UI” logic should have been business logic
Some UI logic can be re-expressed as business logic
Some UI logic can be expressed using metadata
visible
enabled
caption
lookup
validation: required, range, length, etc.
Where should UI logic go?
17. .NET attributes attached to properties to
declaratively express UI logic
Metadata
[ReadOnly(true)]
public string A { get; set; }
[Visible(Property="A", Is="Foo")]
public int B { get; set; }
[OnChange(Send="Currency,Amount", Refresh="Rate,CalculationDate")]
public string D { get; set; }
[Enabled(Property="B", IsGreaterThan=1)]
public string C { get; set; }
18. On page load, metadata is sent to client browser to be
processed:
walk the object graph of a view model
subscribe to change notifications of the targeted properties
on a property’s value change, subscription is triggered and
predefined event handlers are fired
Once the plumbing is done in the framework code,
developers don’t have to write a single line of JavaScript
Metadata
20. .NET attributes aren’t expressive enough
only suitable for extremely simple conditions
difficult to read and understand
Lots of magic strings
difficult to refactor and results in run-time error
Same logic needs to be repeated for multiple properties
Slows page load & increase memory usage (IE8)
Problems Introduced by Metadata
[Visible(Property="Amount" IsGreaterThan=100 And=true
Property2="Code" Property2StartsWith="ISO-")]
public int A { get; set; }
21. Attributes now only specifies the name of a method that
provides the actual logic
The actual logic can now be written in plain C#
statements
Use Roslyn & code generation to parse method bodies
and generate equivalent JavaScript statements
Roslyn & Code Generation
[Visible("IsBVisible")]
public int B { get; set; }
[Enabled("IsCEnabled")]
public string C { get; set; }
23. For each server side model, a dependency manager
JavaScript is generated
Can generate .NET proxy classes using the same analysis
for QA test scripts
The generation is re-runnable and can be integrated into
MSBuild to ensure consistency
Roslyn & Code Generation
public Proxy<int> B { get; set; } public Proxy<string> C { get; set; }
24. .NET attributes aren’t expressive enough
logics now reside in C# methods, although they still need to be
relatively simple, they can be much expressive
Lots of magic strings
remaining magic strings are purely method names, which can be
easily validated using Roslyn or ReSharper SDK
Same logic needs to be repeated for multiple properties
multiple metadata can now point to the same method
Slows page load & increase memory usage (IE8)
subscriptions and event handlers are generated at the compile
time, no runtime parsing and wiring up needed
Roslyn & Code Generation
26. Most developers:
are C# developers
are used to Windows desktop application development
are unfamiliar with JavaScript and CSS
have limited Web development experience
Most of business analysts:
are used to designing for desktop applications
are uncomfortable with Web design paradigm
are new to Responsive design & mobile first design concepts
Existing Employees’ Skillsets
27. Steep learning curve
Web hosting: IIS
Server technologies: ASP.NET, ASP.NET MVC, SignalR
UI technologies: HTML, CSS, cross-browser compatibility
CSS framework: Twitter Bootstrap
3rd party UI controls: jQuery UI, Kendo UI
Programming language: JavaScript
3rd party JS libs: jQuery, Knockout, Durandal, Require JS, Q, etc.
Concepts: async & stateless operations, responsive design
Conventions
Existing Employees’ Skillsets
28. Only a small number of experienced web developers write
raw HTML
The rest of developers write in C#
Only a small number of experienced UI designers edit CSS
No developers touch CSS
A rich set of UI framework elements and templates are
developed to facilitate the above
Say “NO” to HTML & CSS
29. Similar to Html.EditFor(m => m.Name)
A large number of HTML helpers and templates are
developed to ensure:
the correct usage of HTML 5 elements
consistent CSS classes and styling
consistent structure for composite controls
the ease of refactoring when design changes
all visible texts are properly resourced
HTML Helpers & Templates
31. Enforcement is done by keeping track of created UI
elements
can be turned off for “RELEASE”
HTML helpers are also designed to be open and flexible to
support power users to handle occasional one-off cases
HTML Helpers & Templates
32. A rich set of metadata are developed to allow developers
to express JavaScript logic using C# constructs
What happens if you really, really have to write in
JavaScript?
don’t!!!
You write in TypeScript
Say “NO” to JavaScript
33. A language for application-scale JavaScript development
A typed superset of JavaScript that compiles to plain
JavaScript
Any browser, any host, any OS
Open Source
TypeScript
34. High level constructs: module, interface, class, enum
Accessibility: public, private, protected
Inheritance: implements, extends, super, overrides
Types: boolean, number, string, null, any, {}
Anonymous type support: { id: number; name: string }
Generic support: Array<string>, Promise<T>
Casting: id: number = <number>obj.Id;
Function overloading and optional parameters: (extraData?: any)
Variable amounts of arguments: format(pattern: string, …args: any[])
Lambda expression: (str1: string, str2: string) => str1 + str2
CommonJS and AMD support: import, export, require
TypeScript – Language Features
35. Part of Visual Studio 2013 Update 2
Syntax highlighting
Brace matching
Go to declaration
IntelliSense
Auto-complete
Quick info
Compile time type and syntax
checking
Generate .js, .min.js, and .js.map files
TypeScript – Visual Studio IDE Features
36. Type definitions can be created for existing 3rd party
JavaScript libraries
Provides type checking and IntelliSense
DefinitelyTyped contains more than 440 high quality type
definitions for well-known JavaScript libraries: jQuery,
Knockout, Underscore, Node, etc.
The list is growing and updates are frequent
TypeScript – for 3rd Party JS Libraries