I'm building a system which will have a few channels feeding different clients (MonoDroid, MonoTouch, Asp.Net Mvc, REST API)
I'm trying to adopt an SOA archetecture and also trying to adopt the persistence by reachability pattern (http://www.udidahan.com/2009/06/29/dont-create-aggregate-roots/)
My question relates to the design of the archetecture. How best to split the system into discreet chunks to benefit from SOA.
In my model have a SystemImplementation which represents the an installation of the system iteself. And also an Account entity.
The way I initially thought about designing this was to create the services as:
SystemImplementationService - responsible for managing things related to the actual installation itself such as branding, traffic logging etc
AccountService - responsible for managing the users assets (media, network of contacts etc)
Logically the registration of a new user account would happen in AccountService.RegisterAccount where the service can take care of validating the new account (duped username check etc), hashing the pw etc
However, in order to achieve persistence by reachability I'd need to add the new Account to the SystemImplementation.Accounts collection for it to save in the SystemImplementation service automatically (using nhibernate i can use lazy=extra to ensure when i add the new account to the collection it doesn't automatically load all accounts)
For this to happen I'd probably need to create the Account in AccountService, pass back the unsaved entity to the client and then have the client call SystemImplementation.AssociateAccountWithSystemImplementation
So that I don't need to call the SystemImplementation service from the AccountService (as this, correct me if I'm wrong - is bad practise)
My question is then - am i splitting the system incorrectly? If so, how should I be splitting a system? Is there any methodology for defining the way a system should be split for SOA? Is it OK to call a WCF service from in a service:
AccountService.RegisterAccount --> SystemImplementation.AssociateAccountWithSystemImplementation
I'm worried i'm going to start building the system based on some antipatterns which will come to catch me later :)
You have a partitioning issue, but you are not alone, everyone who adopts SOA comes up against this problem. How best to organize or partition my system into relevant pieces?
For me, Roger Sessions is talking the most sense around this topic, and guys like Microsoft are listening in a big way.
The papers that changed my thinking in this can be found at http://www.objectwatch.com/whitepapers/ABetterPath-Final.pdf, but I really recommend his book Simple Architectures for Complex enterprises.
In that book he introduces equivalence relations from set theory and how they relate to the partitioning of service contracts.
In a nutshell,
The rules to formulating partitions can be summarized into five laws:
Partitions must be true partitions.
a. Items live in one partition only, ever.
Partitions must be appropriate to the problem at hand.
a. Partitions only minimize complexity when they are appropriate to the problem
at hand, e.g. a clothing store organized by color would have little value to
customers looking for what they want.
The number of subsets must be appropriate.
a. Studies show that there seems to be an optimum number of items in a
subset, adding more subsets, thus reducing the number of items in each
subset, has very little effect on complexity, but reducing the number of
subsets, thus increasing the number of elements in each subset seems to
add to complexity. The number seems to sit in the range 3 – 12, with 3 – 5
being optimal.
The size of the subsets must be roughly equal
a. The size of the subsets and their importance in the overall partition must be
roughly equivalent.
The interaction between the subsets must be minimal and well defined.
a. A reduction in complexity is dependent on minimizing both the number and
nature of interactions between subsets of the partition.
Do not stress to much if at first you get it wrong, the SOA Manifesto tell us we should value Evolutionary refinement over pursuit of initial perfection .
Good luck
With SOA, the hardest part is deciding on your vertical slices of functionality.
The general principles are...
1) You shouldn't have multiple services talking to the same table. You need to create one service that encompasses an area of functionality and then be strict by preventing any other service from touching those same tables.
2) In contrast to this, you also want to keep each vertical slice as narrow as it can be (but no narrower!). If you can avoid complex, deep object graphs, all the better.
How you slice your functionality depends very much on your own comfort level. For example, if you have a relationship between your "Article" and your "Author", you will be tempted to create an object graph that represents an "Author", which contains a list of "Articles" written by the author. You would actually be better off having an "Author" object, delivered by "AuthorService" and the ability to get "Article" object from the "ArticleService" based simply on the AuthorId. This means you don't have to construct a complete author object graph with lists of articles, comments, messages, permissions and loads more every time you want to deal with an Author. Even though NHibernate would lazy-load the relevant parts of this for you, it is still a complicated object graph.
Related
We're currently designing some data services, which should deliver data to many individual parties and therefore be generic enough to be shared but not to complex to understand.
Generally, it's quite simple, some but we're not at the point, where we're discussing the possibilities for core data, which has some inheritance hierarchies as well as additional data.
For example:
Individual ist just a Individual (no shaping)
Individual is an Employee
Individual is an Event Participant
etc
Also, an Indivivual HAS additional data
Addresses
Phone numbers
etc
Watching several REST best practices documents, for example https://pages.apigee.com/rs/apigee/images/api-design-ebook-2012-03.pdf, the closest to the solution I see is to create endpoints for each inherited Type:
ServiceApi/DataServices/v1/Individuals
ServiceApi/DataServices/v1/Employees
And probably add the "Has"-data with a partial response mechanism. This seems odd, since now the consumer has to know, what subtype he asks for.
Another solution could be to add a lot of endpoints and create a DTO for each requested possibility:
IndividualWithAddresses
IndividualWithPhoneNumbers
EmployeeWithPhoneNumbers
None of these solutions, and neither the others we found, seems appealing. I feel like the big API providers, which surely have a way richer data model, must have had similar discussions. Is there a solution to keep the complexity in check and still stay flexible enough?
Suppose we would like to program efficiently for some reasons and constraints.
Should we put aside OOP?
Let's illustrate with an example
public class CPlayer{
Vector3 m_position;
Quaternion m_rotation;
// other fields
}
public class CPlayerController{
CPlayer[] players;
public CPlayerController(int _count){
players=new CPlayer[_count];
}
public void ComputeClosestPlayer(CPlayer _player){
for(int i=0;i<players.Length;i++){
// find the closest player to _player
}
}
}
If we convert the class to a struct, we can utilize to cache the player array in cache memory and get better performance. When we need to iterate the array in ComputeClosestPlayer function, we know that the players structs were stored consecutively so can come into the cache memory when reading the first element of the array.
public struct CPlayerController{
CPlayer[] players;
public CPlayerController(int _count){
players=new CPlayer[_count];
}
public void ComputeClosestPlayer(CPlayer _player){
for(int i=0;i<players.Length;i++){
// find the closest player to _player
}
}
}
If we want to achieve more performance, we can separate position field from the class:
public Vector3[] m_positions;
So now, only the positions(12 bytes for every of them) are cached in cache memory when we call the function while in previous approach, we have to cache objects that take more memory.
Finally I do not know that it is a standard approach or you avoid it to separate some fields from a class to get better performance and share your approach to get the most performance in strategy games that you have a lot of items and soldiers
Put aside OO design pattern to achieve better performance in strategic
games?
I tend to favor this broad approach of putting aside OOP for visual FX at the central architecture and specifically with an entity-component system like so:
... where the components, in blue, are just data (structs with no functionality of their own). If there's any functionality at all inside a component, it's pure data structure functionality (like the functions you find in std::vector in C++ or ArrayList in C#).
It does allow efficient things to be done more easily, but the primary benefit for me wasn't efficiency. It was the flexibility and maintainability. When I need brand new behavior, I can just make a small local modification a system or add a new component or add a new system when the wide dependencies are flowing towards data rather than abstractions. The need to face cascading design changes has been a thing of the past ever since I embraced this approach.
"No Central Design"
Every new design idea, no matter how crazy, tends to be fairly easy to extend and add to the system without breaking any central designs, since there are no central abstract designs in the first place (or a kind that contain functionality) besides the ECS database itself. And with the exception of dependencies to the ECS database and a handful of components (raw data) in each system, the system is uber decoupled.
And it makes every system easy to reason about from everything like thread safety to what side effects go on where and when. Each system performs a very well-defined role that maps very directly to business requirements. It's harder to reason about designs and responsibilities when you have this instead for the communication/interaction between medium and teeny-sized objects:
... and this above graph is not a dependency diagram. In terms of coupling there might be an abstraction between every object to decouple them that way (ex: Modeling object depending on IMesh, not a concrete mesh), but they still talk to each other, and all that interaction and communication can make it difficult to reason about what's going on as well as come up with the most efficient loopy code.
Meanwhile the first system that has each independent system processing data from a central database in a flat pipeline-style fashion makes it very easy to figure out what's going on as well as implement the loopy critical paths of execution very efficiently. It also makes it so you can sit down and work on a system without knowing what the other systems are doing: all you have to do to implement a physics system is read things like motion components from the database and transform the data correctly. You don't need to know much to implement and maintain the physics system besides a few component types and how to fetch them from the ECS "database".
That also makes it easier to work in a team and also hire new developers who can quickly come up to speed without spending 2 years trying to teach them how the central abstractions in the overall system works just so that they can do their job. They can start doing things as bold and as centrally-impacting to the software's design as introducing a brand new physics engine or rendering engine to the system within weeks.
Efficiency
If we convert the class to a struct, we can utilize to cache the
player array in cache memory and get better performance.
That's only at a granular level where objects start to get in the way of performance. For example, if you try to represent one single pixel of an image with an abstract Pixel object or IPixel interface that encapsulates and hides its data, then it can very easily become a performance barrier even without considering the cost of dynamic dispatch. Such a granular object just tends to force you to work at the granular level of one pixel at a time while fudging about with its public interface, so optimizations like processing the image on the GPU or SIMD processing on the CPU are out when we have this barrier between ourselves and the pixel data.
Aside from that you generally can't code to an interface and expect efficient solutions at this granular of a level (one pixel). We can't hide away concrete details like pixel formats and abstract them away and expect to write efficient video filters that loop through millions of pixels every frame, e.g. At a low enough level, we have to start writing code against concrete details for reasonable efficiency. Abstractions and coding to an interface are helpful as you work towards high-level operations.
But naturally that doesn't apply if you turn Pixel into just raw data stored in an Image. There an image object, which is really a container of often millions of pixels, isn't a practical barrier to achieving a very efficient solution. We don't have to abandon OOP to write very efficient loops. We just might need to do it for the teeniest, most granular objects that store barely any data of their own.
So one alternative strategy is to just model your objects at a coarser level. You don't have to design a Human class. You can design a Humans class which inherits from Creatures. Now the implementation of Humans might consist of loopy multithreaded SIMD code which processes thousands of humans worth of data (ex: SoA fields stored in parallel arrays) at once.
Alternatives to OOP
The appeal to me of abandoning OOP at the broadest level of the design (I still use OOP plenty for the implementation of each subsystem) in favor of value aggregates at the central level as with the case of components in an ECS is mostly the flexibility to me of leaving the data wide open and accessible through a central "database". It makes it easier to just implement the systems you need and effectively access the data without jumping through hoops and going through layers upon layers of abstractions while fighting against the abstractions you designed.
Of course there are downsides, like that your data now has to be very stable (we can't keep changing it) or else your systems will break. And you also have to have a decent system organization to make it so your data is accessed and modified in the minimum number of places to allow you to effectively maintain invariants, reason about side effects, etc. But I've found an ECS does that beautifully just naturally, since it becomes very easy to tell what systems access what components.
In my case, I found a nice fit using ECS for the large-scale design and then when you zoom in to the implementation of a particular system, like a physics or rendering system, then they use OOP to help implement auxiliary data structures and medium-sized objects to make the system's implementation more comprehensible and things like that. I find OOP very useful at the medium kind of scale of complexity, but sometimes very difficult to maintain and optimize at the largest scale.
Hot/Cold Field Splitting and Value Aggregates
Finally I do not know that it is a standard approach or you avoid it
to separate some fields from a class to get better performance and
share your approach to get the most performance in strategy games that
you have a lot of items and soldiers
This is getting a bit C#-specific and I'm more of a C++ and C programmer, but I believe I've read that C# structs, as long as they aren't boxed, can be stored contiguously in an array. That contiguity you get can make a world of difference in terms of reducing cache misses.
Specifically in GC languages, often the initial set of objects allocations can be done quickly using a sequential allocator ("Eden" space in Java, I believe C# does something similar though I haven't read any papers on C#'s implementation details) for very efficient GC implementations. But after the first GC cycle, the memory can then be shuffled around to allow it to be reclaimed on an individual object basis. That loss of spatial locality can then really hurt performance if you need to do very efficient sequential loops. So storing an array of structs or primitive data types like int or float might be a useful optimization in some key loopy areas of your game.
As for the approach of separating fields, that's useful for SIMD processing and hot/cold field splitting. Hot/cold field splitting is separating data fields frequently-accessed away from others which aren't. For example, a particle system might spend the bulk of its time moving particles around and detecting if they collide. It has absolutely no interest in things like a particle's color in that case during the critical paths.
So an effective optimization in that case might be to avoid storing color directly inside a particle, and instead hoist it out and store it in its own separate, parallel array. This way the hot data that is constantly accessed can be loaded into a 64-byte cache line without irrelevant data, like color, being loaded into it needlessly and slowing down the critical paths by making them have to plow through more irrelevant data to get at the relevant data.
All non-trivial optimizations tend to boil down to exchanges that skew performance towards the common case at cost to the rare case. To strike a good bargain and find a good trade, you want to make the common case faster even if it makes the rare case a little bit slower. Beyond blatant inefficiencies, you generally can't make everything fast for everything, though you can achieve something that looks that way and seems super fast for everything to the user if you optimize the common cases, the critical paths, really well.
Memory Access and Representation
If we want to achieve more performance, we can separate position field
from the class:
public Vector3[] m_positions;
This would be working towards an SoA (Structure of Arrays) approach and makes sense if the bulk of your critical loops spend time accessing the position of a player in sequential or random-access patterns but not, say, rotation. If both rotation and position are accessed frequently together in a random-access pattern, a struct storing both using an AoS (array of structures) approach might make the most sense. If they're both accessed predominantly in a sequential access pattern with no random-access, then the SoA might perform better not because it reduces cache misses (it would be close to on par with AoS) but because it can allow more efficient instruction selection by the optimizer when you can, say, load 8 SPFP positions fields at once into an YMM register without rotation fields interleaved with more homogeneous vertical arithmetic in processing loops.
A full-blown SoA approach might even separate your position components. Instead of:
xyzxyzxyzxyz...
It might favor this if the access patterns for critical paths are all sequential and process a large amount of data:
xxxxxxxx...
yyyyyyyy...
zzzzzzzz...
Like so:
float[] m_x;
float[] m_y;
float[] m_z;
That tends to be the most friendly memory layout for all kinds of SIMD instructions (allows you or the optimizer to use SIMD in a way that looks identical to scalar code, only it's being applied to 4+ fields at one time), though you generally want sequential access patterns for this kind of layout. If it's random-access, you might end up with almost three times the cache misses.
As for what you choose, first you have to figure out how you're going to be accessing the data in your most critical loops to understand how to design and represent it very effectively. And you generally have to make some exchanges that will slow down the rare case in favor of the common case, since if you go with an SoA design, you might still have some places in the system that would benefit more from AoS, so you're speeding up the common case using SoA if your critical paths are sequential while slowing down the rare case if your non-critical paths use random access patterns. There's a lot to take in and compromises to be made to come up with the most efficient solutions, and naturally it helps to measure, but also to design things efficiently upfront, you have to think about memory access patterns as well. A good memory layout for one access pattern isn't necessarily good for another.
If in doubt, I'd favor the AoS until you hit a hotspot since it's generally easier to maintain than parallel arrays. Then you might selectively apply SoA optimizations in hindsight. The key is to find the breathing room to do that, which you will find if you design coarser objects like Image, not Pixel, like Humans, not Human, like ParticleSystem, not Particle. If you design little teeny objects you use everywhere, you might find yourself trapped into a sub-optimal representation you can't change without breaking everything.
Finally I do not know that it is a standard approach or you avoid it
to separate some fields from a class to get better performance [...]
Actually it's very common and is at least discussed fairly widely (at least it's not too esoteric knowledge) in fields like computer graphics and gaming when using languages that give you very explicit control over memory layouts like C++. However, the techniques are just as applicable even to languages using GC, since those languages can still give you plenty enough control over memory layouts in ways that matter provided they at least give you something like a struct which can be stored contiguously in an array.
All of this stuff about efficient memory access patterns relates predominantly to contiguity and spatial locality because we're dealing with loops and ideally when we load some data into a cache line, it covers not only data for that one element we're interested in but also the next one, and the next one, and so forth. We want as much relevant data to be there as possible and as little irrelevant data to be there as possible. Most of it becomes pointless (except for temporal locality) if nothing is contiguously stored since we'd be loading irrelevant data all over the place every time we load one element, but just about every language out there gives you some ways to store data in a purely contiguous fashion, even if you can't use objects for that.
I've actually seen a small interactive path tracer written in Java which rivaled the speed of any equally small interactive one written in C or C++. The main thing was that it avoided using objects for critical parts involving BVH traversal and ray/triangle intersection. There it used big arrays of floats and integers, but everything else used OOP. If you apply these types of optimizations discreetly in such languages, then they can start to give extremely impressive results from a performance standpoint without getting bogged down with maintenance issues.
Deciding between struct or class can be really tricky. Structs become value types while classes become reference types. Value types are passed by value, meaning that content of the struct is copied to arrays, function parameters, etc... Reference types are passed by reference, meaning only their reference is passed. The other performance issue might arise from the frequent boxing and un-boxing of value types.
I might not explain it well, fortunately MSDN has a pretty good guide here.
The one thing I would copy here from the guide is this:
✓ CONSIDER defining a struct instead of a class if instances of
the type are small and commonly short-lived or are commonly embedded
in other objects.
X AVOID defining a struct unless the type has all of the
following characteristics:
It logically represents a single value, similar to primitive types (int, double, etc.).
It has an instance size under 16 bytes.
It is immutable.
It will not have to be boxed frequently.
In all other cases, you should define your types as classes.
I have an application that visitors come and go.
I m working with a data provider that gives me information about users such as their gender, age, location, and information about their personalities etc.
Now, i d like to target these users with appropriate content.
In short, I have content and users with their personality information, i need to display the best content that matches their character, personality etc.
I am aware that given a list of content and a user, i will be searching for the best possible content for the user, ie: A* search.
How would you design and implement such application?
Which algorithm(s)/data structures you would use? graphs ? adjacency list? matrix?
I would suggest solving this problem using Bayesian inference.
Bayesian Classifiers
As the problem is currently stated, the only classification of the content that is available is the distribution of the users which have visited it and the characteristics of those users. The joint probability distribution across all user-characteristic dimensions for all users is the classifier for that content.
So how does one use the above information? Given content A with user access distribution B for all users and a target user characteristic profile C, one can compute the probability that the latter user would be interested in content A. If one performs this computation against all content relative to user profile C, one gets a list of interest probability values for all of the content. Sort that list by the probability values to identify the best possible content for the target user.
In many cases, only a subset of user characteristic parameters may be predictive of the value of a given content item to users. This is a common situation for Bayesian classifiers in general and has led to the development of Bayesian networks, which are structured graphs of key variables and their conditional dependencies. Such networks can be modeled via Bayesian inference methods as well.
Bayesian Network Software
The WEKA Data Mining software is an open-source Java library which implements many common classification methods including Bayesian network classifiers, and it is well worth trying out. I can't recommend any specific C# equivalent packages, but a quick web search identified at least one commercial Bayesian package for .NET, Bayes Server.
Recommended Reading
There is a pretty large body of literature surrounding bayesian classifiers, and it is a very sound technique that is use in SPAM filtering, drug discovery, etc. Two books that I can recommend for this are listed below. Bolstad's book is for beginners, while Pearl's book is more advanced.
Bolstad, William M. (2007). Introduction to Bayesian Statistics, Second Edition, John Wiley.
Judea Pearl (2000). Causality: Models, Reasoning, and Inference, Cambridge University Press.
Very interesting question!
You're talking about best possible content. But you didn't mention a measurement. I guess under content you've meant some form of advertisement and “best” means most efficient, i.e. having highest CTR.
So you have a function:
f(gender, age, location, personality, ..., advertisement) -> CTR
Each visit you get fixed gender, age, location, etc. Under fixed I mean: you already have this visitor, you can't vary his age. And you have a parameter that you can change: advertisement. Your goal is to maximize CRT.
Varying advertisements you can gather statistics for CTR under different combinations. Once you have minimal initial knowledge you can try to use optimization theory methods, particularly nonlinear programming to find optimal advertisement parameter for given gender, age, location, etc. Continue to gather CTR statistics to make subsequent decisions more and more precise.
P.S. There was a startup showcase on TechCrunch. They did similar thing and have had fantastic results. So if you will success, think about starting your own business ;)
Sorry for the naive question; I'm still working through understanding DDD. Let's say I have an IOrderService. Is this where I would have a method GetNewOrderID? Or, I guess more generally, what is the right way to allocate new orders in a DDD scenario?
Unless I've misunderstood DDD then it's not a naive question - instead when it's not clear where the responsibility then not enough of the domain has been investigated / understood. Things like:
What is the format of the Order ID,
what information goes into a single
OrderID.
Is there a requirement to
save anything at the point of getting
a new OrderID - like who requested
it, etc.
Is there a requirement
that requested, but unused Order IDs
be freed up?
One or more of these requirements might clarify the situation.
I like to listen to the words that the business use. If they talk about, for instance, catalogue orders and phone orders both being sources of orders, I might have an OrderSource or IOrderSource instead of an OrderService - the last is code-speak instead of business-speak. I might have two implementations, or just one which uses an identifier to say "this is from a phone", depending on how different the processes are.
If the business people talk about getting IDs from their service, then yes, do that. It's more likely though that they talk about receiving an order from an OrderSource, dispatching it to the Warehouse or creating an OrderForm and receiving a ReferenceNumber. That ReferenceNumber may well be the primary key, but it's probably not an Id.
The language that the business use can guide you to write software which matches their process well. Keeps things easy to change and helps you spot if there's an aspect of the domain which could use some learning. The design patterns are all the same that you're used to; I just don't call my code after those if the business have some better terms. See DDD's Ubiquitous Language, and good luck!
In informal conversations with our customer service department, they have expressed dissatisfaction with our web-based CSA (customer service application). In a callcenter, calls per hour are critical, and lots of time is wasted mousing around, clicking buttons, selecting values in dropdown lists, etc. What the dirrector of customer service has wistfully asked for is a return to the good old days of keyboard-driven applications with very little visual detail, just what's necessary to present data to the CSR and process the call.
I can't help but be reminded of the greenscreen apps we all used to use (and the more seasoned among us used to make). Not only would such an application be more productive, but healthier for the reps to use, as they must be risking injury doing data entry through a web app all day.
I'd like to keep the convenience of browser-based deployment and preserve our existing investment in the Microsoft stack, but how can I deliver this keyboard-driven ultra-simple greenscreen concept to the web?
Good answers will link to libraries, other web applications with a similar style, best practices for organizing and prioritizing keyboard shortcut data (not how to add them, but how to store and maintain the shortcuts and automatically resolve conflicts, etc.
EDIT: accepted answers will not be mini-lectures on how to do UI on the web. I do not want any links, buttons or anything to click on whatsoever.
EDIT2: this application has 500 users, spread out in call centers around North America. I cannot retrain them all to use the TAB key
I make web based CSR apps. What your manager is forgetting is now the application is MUCH more complex. We are asking more from our reps than we did 15 years ago. We collect more information and record more data than before.
Instead of a "greenscreen" application, you should focus on making the web application behave better. For example,dont have a dropdown for year when it can be a input field. Make sure the taborder is correct and sane, you can even put little numbers next to each field grouping to indicate tab order. Assign different screens/tabs to F keys and denote them on the screen.
You should be able to use your web app without a mouse at all with no loss of productivity if done correctly.
Leverage the use of AJAX so a round trip to the server doesn't change the focus of their cursor.
On a CSR app, you often have several defaults. you should assign each default a button and allow the csr to push 1 button to get the default they want. this will reduce the amount of clicking and mousing around.
Also very important You need to sit with the CSR's and watch them for a while to get a feel for how they use the app. if you haven't done this, you are probably overlooking simple changes that will greatly enhance their productivity.
body { background: #000; color: #0F0; }
More seriously, it's entirely possible to bind keyboard shortcuts to actions in a web app.
You might consider teaching your users to just use the Tab key - that's how I fill out most web forms. Tab to a select list and type out the first few letters of the option I'm attempting to select. If the page doesn't do goofy things with structure and tabindexes, I can usually fill out most web forms with just the keyboard.
As I had to use some of those apps over time, will give my feedback as a user, FWIW, and maybe it helps you to help your users :-) Sorry it's a bit long but the topic is rather close to my heart - as I had myself to prototype the "improved" interface for such a system (which, according to our calculations, saves very nontrivial amounts of money and avoids the user dissatisfaction) and then lead the team that implemented it.
There is one common issue that I noticed with quite a few of CRMs: there is 20+ fields on the screen, of which typically one uses 4-5 for performing of 90% of operations. But one needs to click through the unnecessary fields anyway.
I might be wrong with this assumption, of course (as in my case there was a wide variety of users with different functions using the system). But do try to sit down with the users and see how they are using the application and see if you can optimize something UI-wise - or, if really it's a matter of not knowing how to use "TAB" (and they really need to use each and every of those 20 fields each time) - you will be able to coach a few of them and check whether this is something sufficient for them - and then roll out the training for the entire organization. Ensure you have the intuitive hotkey support, and that if a list contains 2000 items, the users do not have to scroll it manually to find the right one, but rather can use FF's feature to select the item by typing the start of its text.
You might learn a lot by looking at the usage patterns of the application and then optimizing the UI accordingly. If you have multiple organizational functions that use the system - then the "ideal UI" for each of them might be different, so the question of which to implement, and if, becomes a business decision.
There are also some other little details that matter for the users - sometimes what you'd thought would be the main input field for them in reality is not - and they have an empty textarea eating up half of the screen, while they have to enter the really important data into a small text field somewhere in the corner. Or that in their screen resolution they need the horizontal scrolling (or, scrolling at all).
Again, sitting down with the users and observing should reveal this.
One more issue: "Too fast developer hardware" phenomenon: A lot of the web developers tend to use large displays with high resolution, showing the output of a very powerful PCs. When the result is shown on the CSR's laptop screen at 1024x768 of a year-old laptop, the layout looks quite different from what was anticipated, as well as the rendering performance. Tune, tune, tune.
And, finally - if your organization is geographically disperse, always test with the longest-latency/smallest bandwidth link equivalent. These issues are not seen when doing the testing locally, but add a lot of annoyance when using the system over the WAN. In short - try to use the worst-case scenario when doing any testing/development of your application - then this will become annoying to you and you will optimize its use - so then the users that are in better situation will jump in joy over the apps performance.
If you are in for the "green screen app" - then maybe for the power users provide a single long text input field where they could type all the information in the CLI-type fashion and just hit "submit" or the ENTER key (though this design decision is not something to be taken lightly as it is a lot of work). But everyone needs to realize that "green-screen" applications have a rather steep learning curve - this is another factor to consider from the business point of view, along with the attrition rate, etc. Ask the boss how long does the typical agent stay at the same place and how would the productivity be affected if they needed a 3-month term to come to full speed. :) There's a balance that is not decided by the programmers alone, nor by the management alone, but requires a joint effort.
And finally a side note in case you have "power users": you might want to take a look at conkeror as a browser - though fairly slow in itself, it looks quite flexible in what it can offer from the keyboard-only control perspective.
I can't agree with the others more when they say the first priority of the redesign should be going and talking to / observing your users and see where they have problems. I think you would see far more ROI if you find out the most common tasks and the most common errors your users make and streamline those within the bounds of your existing UI. I realize this isn't an easy thing to do, but if you can pull it off you'll have much happier users (since you've solved their workflow issues) and much happier bosses (since you saved the company money by not having to re-train all the users on a completely new UI).
After reading everyone else's answers and comments, I wanted to address a few other things:
EDIT: accepted answers will not be mini-lectures on how to do UI on the web. I do not want any links, buttons or anything to click on whatsoever.
I don't mean to be argumentative, but this sounds like you've already made up your mind without having thought of the implications on the users. I can immediately see a couple pitfalls with this approach:
A greenscreen-esque UI may not be
more productive for your users. For
example, what's the average age of
your users? Most people 25 and
younger have had little to no
exposure to these types of UIs.
Suddenly imposing this sort of
interface on them could cause a
major backlash from your users. As an example, look at what happened
when Facebook decided to change its
UI to the "stream" concept - huge
outrage from the users!
The web wasn't really designed with this sort of interface in mind. What I mean is that people are not used to having command-line-like interfaces when they visit a website. They expect visual medium (images, buttons, links, etc.) in addition to text. Changing too drastically from this could confuse your users.
Programming this type of interface will be tough. As in my last point, the web doesn't play well with command-line-like or text-only interfaces. Things like function keys, keyboard shortcuts (like ctrl- and alt-) are all poorly and inconsistently supported which means you'll have to come up with your own ways of accessing standard things like help (since F1 will map to the web browser's help, not your app's).
EDIT2: this application has 500 users, spread out in call centers around North America. I cannot retrain them all to use the TAB key
I think this argument is really just a strawman. If you are introducing a wholly new UI, you're going to have to train your users on it. Really, it should be assumed that any change to your UI will require training in one form or another. Something simple like adding tab-navigation to the UI is actually comparatively small in the training department. If you did this it would be very easy to send out a "handy new feature in the UI" email, or even better, have some sort of "tip of the day" (that users can toggle off, of course) which tells them about cool timesaving features like tab navigation.
I can't speak for the other posters here, but I did want to say that I hope you don't think we're being too argumentative here as that's not our (well OK, my) intent. Rather the reaction comes from us hearing the idea for your UI and not being convinced that it is necessarily the best thing for your users. You are fully welcome to say I'm wrong and that this is what your users will benefit most from; but before you do, just remember that at the end of the day it's your users who matter most and if they don't buy in to your new UI, no one will.
It's really more of a keyboard-centric mentality when developing. I use the keyboard for as much as possible and the apps I build tend to show that (so I can quickly go through my use cases).
Something as simple as getting the tab order correct could be all your app needs (I guess I'm not sure if you can set this in ASP.NET...). A lot of controls will auto-complete for the rest.