Lift is composed on discrete elements. Lift support the likes of JNDI, JTA, JPA, etc. The fact that you're not forced to uses these elements of J/EE is a strong indication of Lift's modular design.

Lift's view philosophy is "let the developer decide." Lift offers a templating mechanism that does not allow any logic code in the view, a view mechanism based on executing Scala code and Scala's XML literals, and a view mechanism based on Scalate. If you choose the XML templating mechanism, then you choose how much, if any, mark-up belongs in your business logic. You cannot express any business logic in Lift's XML templates.

Lift's Object - Persistence philosophy is "let the developer decide." Lift has Mapper which is an ActiveRecord style object relational mapper. It gets the job done for small projects. Lift support JPA. Lift has a Record abstraction that supports shuttling objects into and out of relational databases, into and out of NoSQL stores (Lift includes native support for CouchDB and MongoDB, but the adapter layers are a few hundred lines of code, so if you want Cassandra or something else, it's not a lot of work to get it.) Basically, Lift the Web Framework has no dependence on how objects are materialized into a session. Further, the session and request cycles are open such that inserting transaction hooks into the request/response cycle is simple.

Lift's philosophy is "the server team needs to know one language, not multiple languages." This means that configuration is done via Scala. This means that we didn't have to implement 40% of Java's language constructs in XML syntax to create flexible configuration options. It means that the compiler syntax and type-checks the configuration data so you don't get any weird XML parsing or incorrect data at runtime. In means that you don't have to have IDEs that understand the particulars of the annotations that you're using based on the library that you're using.

Source: Lift