Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Spring MVC in Practice: Clear Boundaries for Teams

Posted on August 13, 2008 By Luis Fernandez
\n

We were pushing a release after midnight, the kind that makes pizza feel like a requirement and not a snack. Tomcat sat there staring at us, almost smug. The bug we were chasing lived in the no mans land between what the browser sent and what our service layer expected. Two developers from the web team blamed the domain logic. The folks in the service side blamed the controller. I opened a controller and found three hundred lines that tried to be a traffic cop, a translator, and a part time business expert. That night I promised myself this would not happen again. Not because I love sleep, which I do, but because Spring MVC works best when teams draw clear lines and stick to them.

\n\n\n\n

The buzz today is about the iPhone 3G, the Olympics on every TV, and another round of Java is dead jokes. Meanwhile, Spring keeps shipping steady ideas that help us ship steady code. If your app rides on Spring MVC 2.5 and you are juggling JSP, Tiles, maybe some FreeMarker on the side, this is for you. You can get a lot of peace by setting clean boundaries. Your future self will thank you. Your team will get their nights back. And your code base will start to read like a novel with chapters instead of a mystery with missing pages.

\n\n\n\n

The story we keep repeating

\n\n\n\n

Every big web project I have seen ends up with the same pressure. The launch date is firm, the checklist is longer than it looks, and the controller seems like the easiest place to just make it work. Someone adds formatting logic there because it is fast. Then someone adds validation there because it is right at hand. Then you need a security check and it sneaks in too. Before long the controller is doing everything, the service is barely used, and the next bug is one more if statement away.

\n\n\n\n

Teams ship like this for a while. Then speed drops. Onboarding a new dev takes weeks. Tests get slow or worse, do not exist. You know the rest. If any of this feels close to home, keep reading. We can fix it without a rewrite. This is about moving code to the right place and making Spring MVC do the heavy lifting it already knows how to do.

\n\n\n\n

Technical middle: what goes where in Spring MVC

\n\n\n\n

Start with a simple map in your head. Controllers coordinate. Services decide. Repositories fetch and store. Views show. If you keep that map, you already solve half your mess. The other half is about a few Spring features that reward discipline.

\n\n\n\n

Controllers stay thin. A controller should accept a request, bind it to a command object, call a service, drop results in the model, and pick a view name. That is it. No math. No business rules. No I can just parse this date here. When a controller feels big, it is telling you that you forgot a service method or a helper that belongs in the domain side of the world.

\n\n\n\n

Services own the rules. The service layer should validate business rules, run transactions, and talk to repositories. If something is about the business and not about HTTP, it belongs here. Keep services free from servlet types. No HttpServletRequest inside a service method. That choice alone makes testing way easier because JUnit does not want a servlet container in the room.

\n\n\n\n

Repositories own data access. Whether you are on JDBC templates, Hibernate, or JPA, repositories should do persistence and nothing else. No formatting. No security checks. They feed services and keep queries in one place.

\n\n\n\n

URL design speaks for the app. Use clear nouns. Keep verbs for actions that are not the basic read and create. You do not need to convince anyone that pure REST is a religion, but a simple pattern helps everyone. Think of /orders, /orders new, /orders id. It helps your QA write test plans, helps your front folks keep links tidy, and helps your brain. In Spring MVC 2.5, @RequestMapping gives you a nice way to map clear paths and methods to controllers. Even without going deep into REST, use GET for reads and POST for changes. You will sleep better.

\n\n\n\n

Binding and validation live together. When users post forms, Spring binds fields into objects. Let that happen. Do not bind in a service. If you need a custom way to turn strings into dates or money, use PropertyEditors and register them with @InitBinder. Keep BindingResult close to the form object in the controller. Then pass a clean object to the service. At this point Bean Validation as a spec is not final, but Hibernate Validator already lets you put constraints on your domain classes. It keeps rules near the data and gets you consistent error messages. Win win.

\n\n\n\n

View tech is not your business logic. Whether you use JSP with JSTL, Tiles or SiteMesh, or try FreeMarker, fight the urge to compute in the view. Views format and present. They do not make decisions that change outcomes. They should not chase the database. Feed them with model attributes that are ready to show. If a view needs a lookup list, the controller places it in the model. If a table needs pagination, compute that in the service and give the view a simple page object.

\n\n\n\n

Exceptions get one door. Centralize them. Spring MVC has HandlerExceptionResolver and the simple resolver that maps exceptions to views. Pick one place for mapping domain exceptions to nice error pages or JSON bits. Do not catch and swallow exceptions in controllers. Let them bubble to your central resolver where you can log once and respond cleanly.

\n\n\n\n

Cross cutting concerns use interceptors or filters. If you need logging around requests, locale tweaks, or pre authorization checks that do not belong in the business layer, HandlerInterceptor or a servlet filter is your friend. Put that code there and free your controllers from repeating the same checks on every method.

\n\n\n\n

Testing is part of the design. You can test controllers with Spring test support and mock requests. Keep controllers small and the tests stay small. You can test services with real transactions against an in memory database or a test schema on your machine. Put your test application context under src test resources, split by layer if needed. Context caching in Spring test will keep runs fast. The secret is this simple rule. No servlet types in services. No database calls in controllers. That separation kills most test pain.

\n\n\n\n

Configuration is a contract. Keep your contexts tidy. One for web, one for services, one for data. The web context wires controllers and view resolvers. The service context wires services and cross cutting concerns like transactions. The data context wires data sources, templates, and entity managers. Load the web context last so it can see the rest. Externalize secrets and tweakables with PropertyPlaceholderConfigurer and plain files on disk. Your ops person will thank you when they can change a JDBC URL without repacking your war.

\n\n\n\n

Security sits at the edges. Spring Security 2.0 makes it pretty straight. Keep URL rules in the config. Keep method rules near services. Do not sprinkle security checks manually inside controllers or repositories. When you put security in one place, you can reason about it. When you scatter it, you start guessing.

\n\n\n\n

Internationalization early, not later. If your app talks to more than one region, wire a MessageSource and a LocaleResolver right now. Use message codes in views and controllers. You avoid a lot of hardcoded text, and your testers can switch locales without hacks. For many apps, a cookie based resolver keeps users happy. For simpler ones, accept header is fine.

\n\n\n\n

File uploads and friends. Stick to Commons FileUpload through Spring MultipartResolver. Keep files away from the controller code as soon as possible. Hand them to a service that validates type and size and moves them to a safe spot. Then store only metadata or a pointer. Disk fills faster than you think.

\n\n\n\n

Performance without hero moves. Use redirect after post to avoid double submits. Cache read only pages with a filter or at the template layer if your content fits that model. If you are on Hibernate, second level cache with Ehcache is a good start. Keep views light. Do not fetch a hundred columns when you need five. And log with SLF4J so you can swap backends without a chase.

\n\n\n\n

Team play: clear boundaries mean faster teams

\n\n\n\n

Now the people side. You can write all the guides you want, but teams follow what the code teaches. If controllers are thin and services carry the rules, new devs learn that rhythm in a week. If you keep one folder for controllers and one for views and one for services, your project tree teaches the same lesson.

\n\n\n\n

Make contracts visible. The web team needs clear forms and clear model attributes. The service team needs clear inputs and outputs. Write short notes in a shared doc that lists controller endpoints with fields in and fields out. It is not a novel. It is a page the team uses every day. Build acceptance tests with Selenium RC for the big flows and unit tests for rules. The goal is to catch surprises at the border, not after a deploy.

\n\n\n\n

Keep ownership clear. One person owns the URL map. One person owns message codes and i18n. One person owns exception mapping. You can rotate each quarter. You just want someone watching each seam of the app. Problems grow where there is no gardener.

\n\n\n\n

Designers will thank you if you give them stable view names and folders that match the navigation. They can work in static HTML and you can wire those templates as JSP or FreeMarker without a surprise. Agree on a simple naming rule for CSS classes and keep it. No random ids in views. Predictable names make both sides faster.

\n\n\n\n

For process, keep it boring and steady. Two week sprints. Demos on Fridays. Small pull requests if you are trying Git, or small commits in Subversion with clear messages if that is your world. A Jenkins or Hudson job that runs all tests and complains loudly. A staging server that mirrors production enough to be trusted. No cowboy edits on the box. A release checklist you can do half asleep.

\n\n\n\n

Onboarding is where you feel the payoff. Hand a new dev a short guide. How to run the app. Where controllers live. Where services live. How to write a controller test. A list of ten message codes they will touch in their first ticket. By day three they ship a real change. Morale goes up. Bugs go down.

\n\n\n\n

To keep the code honest, run a short boundary review every sprint. Pick a random controller and ask, is this thin. Pick a service and ask, is there any servlet class in here. Pick a repository and ask, is there any formatting in here. Celebrate the wins. Fix the leaks. It takes ten minutes and you learn more than you expect.

\n\n\n\n

Signs you are on the right track

\n\n\n\n
  • Controllers under one hundred lines most of the time
  • Services that read like business stories with clear method names
  • Repositories that return domain objects without extra fluff
  • Views that have no ifs tied to core rules
  • Tests that run in minutes and fail where the bug lives
  • Configuration split into web, service, data with few surprises
  • Security rules that you can read top to bottom in one file
  • One list of URLs that both QA and developers use
\n\n\n\n

A quick field guide to common traps

\n\n\n\n
  • Fat controllers: You felt the pain already. Move rules to services. Use helpers only when they are not business rules.
  • Validation everywhere: Put form validation next to binding. Put business validation in services. Do not mix the two.
  • View logic creeping in: If you find yourself looping to compute something that changes results, stop and move it out.
  • Parameter soup: Bind to a command object instead of passing a dozen strings to a service.
  • Hidden exceptions: Catch only when you can add value. Let global handlers do the rest.
  • Security in five places: Move rules to Spring Security and method annotations. Delete custom checks in random spots.
\n\n\n\n

Your one week challenge

\n\n\n\n

I like bets you can win. Here is a seven day plan to make a dent without a risky rewrite.

\n\n\n\n
  • Day 1: Draw your map. List controllers, services, repositories. Write the map on a shared doc. Nothing fancy.
  • Day 2: Pick the fattest controller. Move one real rule to a service. Add a simple controller test and a service test.
  • Day 3: Add a global exception resolver. Remove two local try catch blocks that only log and rethrow.
  • Day 4: Centralize URL names. Create a short doc that lists them. Share it with QA and the web folks.
  • Day 5: Wire a message source and move hardcoded text in one view to message codes. Teach the team how to add new ones.
  • Day 6: Add an interceptor for logging request ids or user ids. Remove repeated logging from three controllers.
  • Day 7: Sit with the team and review what shifted. Pick the next two controllers to thin and two services to test.
\n\n\n\n

By next week you will have a cleaner slice of your app, a shared map, and a rhythm you can keep. That is usually all a team needs to turn the ship.

\n\n\n\n

Final thought and a question for you

\n\n\n\n

Spring MVC gives us a frame that rewards focus. When controllers coordinate, services decide, and views show, work gets simple. Projects feel smaller. People argue less. Releases stop breaking at midnight. It is not magic. It is just clear boundaries that match how the web works and how teams think.

\n\n\n\n

Your turn. Take one controller you regret and trim it. Move real rules down. Add a global exception handler. Then tell me in the comments what piece gave you the biggest boost. If you want, share one URL map you are proud of. We can learn from each other and get back to watching late night events without a laptop on the couch.

\n\n\n\n

Keywords for the curious: Spring MVC best practices, thin controllers, service layer design, repository pattern, URL design, validation and binding, HandlerInterceptor, global exception handling, Spring Security 2.0, JSP JSTL, FreeMarker, i18n MessageSource.

\n
Engineering Management Software Architecture Software Engineering

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes