One important aspect to design is to manage dependencies. Circular dependencies will kill you - team A needs B's code to compile and B needs A's code to compile. Look at your modules carefully and decide which way you want compile time dependencies to run. It's not necessarily the same direction as the flow of control or the flow of data.
To work in separate teams with separate code baselines
you should try to aim for stable interfaces. Once you publish a method to another team, try hard not to change the signature. To really enforce discipline you might have each team make compiled jars available to the others, but no source code. Imagine you are separate companies selling shrink-wrapped products.
It's a neat strategy to implement the public API for each module in a 'facade' class that you can throw together very quickly so the other teams can compile. You can even stub out methods so other teams can call it and get some kind of hard-coded results. Then you can implement the real logic at your own pace. I worked on one project with this approach and we could call the ModuleX guys and say "Hey, I need a new getWidget method from you". They could add it to the interface and publish it with stub data in a matter of minutes. It might not actually do anything for a couple days, though.
Of course that sounded the opposite of stable interface, didn't it? If everybody is working closely together you can negotiate a change at any time.
Any of that sound useful? Scroll down to the OO, UML, etc forum to talk more about design ideas around dependencies and the facade concept.
[ October 06, 2005: Message edited by: Stan James ]