Try to keep this as simple as possible and interfaces well defined and documented. Maintaining and debugging a complex system in production easily turns into hell. So if there is a simple and a complex approach, think twice before you go with the complex one.
Defining Services
I think the first step is to identify services and their dependencies: Static Content, Authentication, Local Chat, Global Chat Channels, Regional Chat Channels, Friend's List, Guilds, Bag/Inventory, Auction House, Global Map, World, ...
Then for each of these services decided if the client may talk to them directly. For example it is pretty easy to let the client talk directly with the servers responsible for Global Chat Channels. The world servers don't have to be involved in chat messages at all. Regional Chat can be implemented in the same way, but the world servers have to tell the chat servers when players change regions. Again, they don't have to care about the messages.
The third step is to think about load balancing within a service. For example global and regional chat channels can be split across multiple servers based on their name. It is probably a good idea to not hard code this split into the client, but provide a lookup service.
World Servers
The most difficult part are usually the world servers, so I am starting with a simple approach. It is probably a good idea to let the client talk directly to the server responsible for the region he is in. So on login or region crossing the client has to be told to which server to connect to.
The simple approach is to split the world into independent regions. With independent regions I mean that a player cannot look from one part into another and monsters cannot cross parts. Those regions are different from the regions player see based on the landscape and story of the outside world. Usually most monsters are in dungeons and players tend to accept that they have to walk through a gateway to enter a dungeon. Especially if those dungeons are instantiated on a per player group basis. Other examples on the outside world are different continents and valleys enclosed by high mountains.
A continuous world approach gets complex really quickly, so it makes sense to plan it well: What information does the client need? Which information do the servers have to share? The player will mostly interact only with the objects (including monsters and NPCs) in the same region. You can cheat by placing objects out of click range from the zone border. This means that the client is mostly interested in read only information for neighboring zones. For these cases the zone servers don't have to coordinate anything except for the permission check that the player is close enough to connect to a neighboring zone.
This leaves only a very small number of difficult cases in which objects or actions have to cross a server border. Which is a good thing because those cases such as arrows and spells are performance critical. It may be a good idea to split combat into attacking and defending. So the server of a spell-caster will define the attack parameters including the position of the caster. The server of the defender will get the message about the attack and calculate the impact. The server of the attacker does not need to know about the impact; the client will learn about it using his read only connection.
Depending on how complex your player model is, it may take a couple of seconds to transfer it to another server (Second Life has a huge problem with this). The issue can be mitigated by preparing the transfer in advance when the player gets close to a virtual border. So that most of the player data is already cached on the destination server when the actual handover happens.
Summary
Divide the problem by defining different services that can be split across servers with little dependences. As next step look at how to do load balance within the critical services. Delegate balancing work to the client by instructing it to connect directly to the relevant servers (obviously the servers have to check permissions). Keep it as simple as possible, document the responsibilities of the various services and servers well, provide the option to enable debug output.
PS: Some of these techniques can be used to improve reliability. And you should keep that in mind because using many servers implies a much higher risk of things breaking; not only in the software but also at the hardware level.